io.restassured.internal.RestAssuredResponseOptionsGroovyImpl.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rest-assured Show documentation
Show all versions of rest-assured Show documentation
Java DSL for easy testing of REST services
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.restassured.internal
import groovy.xml.StreamingMarkupBuilder
import io.restassured.assertion.CookieMatcher
import io.restassured.common.mapper.DataToDeserialize
import io.restassured.common.mapper.TypeRef
import io.restassured.config.DecoderConfig
import io.restassured.config.RestAssuredConfig
import io.restassured.filter.log.LogDetail
import io.restassured.filter.time.TimingFilter
import io.restassured.http.Cookie
import io.restassured.http.Cookies
import io.restassured.http.Header
import io.restassured.http.Headers
import io.restassured.internal.http.CharsetExtractor
import io.restassured.internal.mapping.ObjectMapperDeserializationContextImpl
import io.restassured.internal.mapping.ObjectMapping
import io.restassured.internal.print.ResponsePrinter
import io.restassured.internal.support.CloseHTTPClientConnectionInputStreamWrapper
import io.restassured.internal.support.Prettifier
import io.restassured.mapper.ObjectMapper
import io.restassured.mapper.ObjectMapperDeserializationContext
import io.restassured.mapper.ObjectMapperType
import io.restassured.path.json.JsonPath
import io.restassured.path.json.config.JsonPathConfig
import io.restassured.path.xml.XmlPath
import io.restassured.path.xml.XmlPath.CompatibilityMode
import io.restassured.path.xml.config.XmlPathConfig
import io.restassured.response.ResponseBody
import io.restassured.response.ResponseBodyData
import io.restassured.response.ResponseOptions
import org.apache.commons.lang3.StringUtils
import java.lang.reflect.Type
import java.nio.charset.Charset
import java.util.concurrent.TimeUnit
import static io.restassured.internal.common.assertion.AssertParameter.notNull
import static io.restassured.path.json.config.JsonPathConfig.jsonPathConfig
import static io.restassured.path.xml.config.XmlPathConfig.xmlPathConfig
import static org.apache.commons.lang3.StringUtils.containsIgnoreCase
import static org.apache.commons.lang3.StringUtils.isBlank
class RestAssuredResponseOptionsGroovyImpl {
private static final String CANNOT_PARSE_MSG = "Failed to parse response."
public static final String BINARY = "binary"
private static final long NO_RESPONSE_TIME = -1
def responseHeaders
def Cookies cookies
def content
def contentType
def statusLine
def statusCode
def sessionIdName
def Map filterContextProperties
def connectionManager;
def String defaultContentType
def ResponseParserRegistrar rpr
def DecoderConfig decoderConfig
def boolean hasExpectations
def RestAssuredConfig config
public void parseResponse(httpResponse, content, hasBodyAssertions, ResponseParserRegistrar responseParserRegistrar) {
parseHeaders(httpResponse)
parseContentType(httpResponse)
parseCookies()
parseStatus(httpResponse)
if (hasBodyAssertions) {
parseContent(content)
} else {
this.content = content
}
hasExpectations = hasBodyAssertions
this.rpr = responseParserRegistrar
this.defaultContentType = responseParserRegistrar.defaultParser?.getContentType()
}
def parseStatus(httpResponse) {
statusLine = httpResponse.statusLine.toString()
statusCode = httpResponse.statusLine.statusCode
}
def parseContentType(httpResponse) {
try {
contentType = httpResponse.contentType?.toString()
} catch (IllegalArgumentException e) {
// No content type was found, set it to empty
contentType = ""
}
}
def parseCookies() {
if (headers.hasHeaderWithName("Set-Cookie")) {
cookies = CookieMatcher.getCookies(headers.getValues("Set-Cookie"))
}
}
def parseHeaders(httpResponse) {
def headerList = [];
httpResponse.headers.each {
def name = it.getName()
def value = it.getValue();
headerList << new Header(name, value)
}
this.responseHeaders = new Headers(headerList)
}
private def parseContent(content) {
try {
if (content instanceof InputStream) {
this.content = convertToByteArray(content)
} else if (content instanceof Writable) {
this.content = toString(content)
} else if (content instanceof String) {
this.content = content
} else {
this.content = convertToString(content)
}
} catch (IllegalStateException e) {
throw new IllegalStateException(CANNOT_PARSE_MSG, e)
}
}
// TODO: Handle namespaces ??
def toString(Writable node) {
def writer = new StringWriter()
writer << new StreamingMarkupBuilder().bind {
// mkp.declareNamespace(dc: "http://purl.org/dc/elements/1.1/")
mkp.yield node
}
return writer.toString();
}
String print() {
def string = asString();
content = string
println string
string
}
String prettyPrint(ResponseOptions responseOptions, ResponseBody responseBody) {
def body = new Prettifier().getPrettifiedBodyIfPossible(responseOptions, responseBody)
content = body
println body
body
}
def peek(ResponseOptions responseOptions, ResponseBody responseBody) {
ResponsePrinter.print(responseOptions, responseBody, System.out, LogDetail.ALL, false);
}
def prettyPeek(ResponseOptions responseOptions, ResponseBody responseBody) {
ResponsePrinter.print(responseOptions, responseBody, System.out, LogDetail.ALL, true);
}
String asString() {
asString(false)
}
def String asString(boolean forcePlatformDefaultCharsetIfNoCharsetIsSpecifiedInResponse) {
charsetToString(findCharset(forcePlatformDefaultCharsetIfNoCharsetIsSpecifiedInResponse))
}
def boolean isInputStream() {
content instanceof InputStream
}
InputStream asInputStream() {
if (content == null || content instanceof InputStream) {
new CloseHTTPClientConnectionInputStreamWrapper(config.getConnectionConfig(), connectionManager, content)
} else {
content instanceof String ? new ByteArrayInputStream(convertStringToByteArray(content)) : new ByteArrayInputStream(content)
}
}
def byte[] convertStringToByteArray(string) {
string.getBytes(findCharset())
}
byte[] asByteArray() {
if (content == null) {
return new byte[0];
}
if (hasExpectations) {
return content instanceof byte[] ? content : content.getBytes(findCharset())
} else if (content instanceof byte[]) {
content
} else if (content instanceof String) {
convertStringToByteArray(content)
} else {
content = convertStreamToByteArray(content)
content
}
}
def T "as"(Type cls, ResponseBodyData responseBodyData) {
def charset = findCharset();
String contentTypeToChose = findContentType {
throw new IllegalStateException("""Cannot parse content to $cls because no content-type was present in the response and no default parser has been set.\nYou can specify a default parser using e.g.:\nRestAssured.defaultParser = Parser.JSON;\n
or you can specify an explicit ObjectMapper using as($cls, );""")
}
return ObjectMapping.deserialize(responseBodyData, cls, contentTypeToChose, defaultContentType, charset, null, config.getObjectMapperConfig())
}
def T "as"(Type cls, ObjectMapperType mapperType, ResponseBodyData responseBodyData) {
notNull mapperType, "Object mapper type"
def charset = findCharset()
return ObjectMapping.deserialize(responseBodyData, cls, null, defaultContentType, charset, mapperType, config.getObjectMapperConfig())
}
def T "as"(Type cls, ObjectMapper mapper) {
notNull mapper, "Object mapper"
def ctx = createObjectMapperDeserializationContext(cls)
return mapper.deserialize(ctx) as T
}
def T "as"(TypeRef typeRef, ResponseBodyData responseBodyData) {
notNull typeRef, "Type ref"
return "as"(typeRef.getType(), responseBodyData)
}
def String findCharset() {
return findCharset(false)
}
def String findCharset(boolean forcePlatformDefaultCharsetIfNoCharsetIsSpecifiedInResponse) {
String charset = CharsetExtractor.getCharsetFromContentType(isBlank(contentType) ? defaultContentType : contentType)
if (charset == null || charset.trim().equals("")) {
if (decoderConfig == null || forcePlatformDefaultCharsetIfNoCharsetIsSpecifiedInResponse) {
return Charset.defaultCharset().toString()
} else {
charset = decoderConfig.defaultCharsetForContentType(contentType)
}
}
if (StringUtils.equalsIgnoreCase(charset, BINARY)) {
charset = decoderConfig.defaultCharsetForContentType(contentType)
}
return charset;
}
def Cookies detailedCookies() {
if (cookies == null) {
return new Cookies()
}
return cookies
}
def Cookies getDetailedCookies() {
return detailedCookies()
}
def Cookie detailedCookie(String name) {
return detailedCookies().get(name)
}
def Cookie getDetailedCookie(String name) {
return detailedCookie(name)
}
Headers headers() {
return responseHeaders ?: new Headers()
}
Headers getHeaders() {
return headers()
}
String header(String name) {
notNull(name, "name")
return responseHeaders.getValue(name)
}
String getHeader(String name) {
return header(name)
}
Map cookies() {
def cookieMap = [:]
cookies.each { cookie ->
cookieMap.put(cookie.name, cookie.value)
}
return Collections.unmodifiableMap(cookieMap)
}
Map getCookies() {
return cookies()
}
String cookie(String name) {
notNull(name, "name")
return cookies == null ? null : cookies.getValue(name)
}
String getCookie(String name) {
return cookie(name)
}
String contentType() {
return contentType
}
ResponseParserRegistrar getRpr() {
return rpr
}
RestAssuredConfig getConfig() {
return config
}
String getContentType() {
return contentType
}
String statusLine() {
return statusLine
}
int statusCode() {
return statusCode ?: -1
}
String getStatusLine() {
return statusLine()
}
String sessionId() {
return getSessionId()
}
String getSessionId() {
return cookie(sessionIdName)
}
int getStatusCode() {
return statusCode()
}
JsonPath jsonPath() {
jsonPath(jsonPathConfig().charset(findCharset()).
jackson1ObjectMapperFactory(config.getObjectMapperConfig().jackson1ObjectMapperFactory()).
jackson2ObjectMapperFactory(config.getObjectMapperConfig().jackson2ObjectMapperFactory()).
gsonObjectMapperFactory(config.getObjectMapperConfig().gsonObjectMapperFactory()).
numberReturnType(config.getJsonConfig().numberReturnType()));
}
JsonPath jsonPath(JsonPathConfig config) {
notNull(config, "JsonPathConfig")
new JsonPath(asString()).using(config)
}
XmlPath xmlPath() {
xmlPath(CompatibilityMode.XML)
}
XmlPath xmlPath(XmlPathConfig config) {
newXmlPath(CompatibilityMode.XML, config)
}
XmlPath xmlPath(CompatibilityMode compatibilityMode) {
notNull(compatibilityMode, "Compatibility mode")
newXmlPath(compatibilityMode)
}
XmlPath htmlPath() {
return xmlPath(CompatibilityMode.HTML)
}
def T path(String path, String... arguments) {
notNull path, "Path"
if (arguments?.length > 0) {
path = String.format(path, arguments);
}
def contentType = findContentType {
throw new IllegalStateException("""Cannot invoke the path method because no content-type was present in the response and no default parser has been set.\n
You can specify a default parser using e.g.:\nRestAssured.defaultParser = Parser.JSON;\n""")
};
if (containsIgnoreCase(contentType, "xml")) {
return xmlPath().get(path)
} else if (containsIgnoreCase(contentType, "json")) {
return jsonPath().get(path)
} else if (containsIgnoreCase(contentType, "html")) {
return newXmlPath(CompatibilityMode.HTML)
}
throw new IllegalStateException("Cannot determine which path implementation to use because the content-type $contentType doesn't map to a path implementation.")
}
def long time() {
if (filterContextProperties?.containsKey(TimingFilter.RESPONSE_TIME_MILLISECONDS)) {
filterContextProperties.get(TimingFilter.RESPONSE_TIME_MILLISECONDS)
} else {
NO_RESPONSE_TIME
}
}
def long timeIn(TimeUnit timeUnit) {
notNull timeUnit, TimeUnit.class
def time = time()
if (time != NO_RESPONSE_TIME && timeUnit != TimeUnit.MILLISECONDS) {
time = timeUnit.convert(time, TimeUnit.MILLISECONDS)
}
time
}
private convertToByteArray(InputStream stream) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
try {
while ((nRead = stream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
} finally {
stream.close()
}
return buffer.toByteArray();
}
private String convertToString(Reader reader) {
if (reader == null) {
return "";
}
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
reader?.close();
}
return writer.toString();
}
private static byte[] convertStreamToByteArray(InputStream is) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
int nRead;
byte[] data = new byte[16384];
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
} finally {
buffer?.close();
is?.close();
}
return buffer.toByteArray();
}
private String findContentType(Closure closure) {
def contentTypeToChose = null
if (contentType == "") {
if (defaultContentType != null) {
contentTypeToChose = defaultContentType
} else {
closure.call()
}
} else if (rpr.hasCustomParserExcludingDefaultParser(contentType)) {
contentTypeToChose = rpr.getNonDefaultParser(contentType).contentType
} else {
contentTypeToChose = contentType
}
return contentTypeToChose
}
private def newXmlPath(CompatibilityMode xml) {
newXmlPath(xml, xmlPathConfig().charset(findCharset()).
features(config.getXmlConfig().features()).
properties(config.getXmlConfig().properties()).
declareNamespaces(config.getXmlConfig().declaredNamespaces()).
jaxbObjectMapperFactory(config.getObjectMapperConfig().jaxbObjectMapperFactory()))
}
private def newXmlPath(CompatibilityMode mode, XmlPathConfig config) {
notNull(config, "XmlPathConfig")
new XmlPath(mode, asString()).using(config)
}
def charsetToString(charset) {
if (content == null) {
return ""
}
if (content instanceof String) {
content
} else if (content instanceof byte[]) {
new String(content, charset)
} else {
content = convertStreamToByteArray(content)
new String(content, charset)
}
}
private ObjectMapperDeserializationContext createObjectMapperDeserializationContext(Type cls) {
def ctx = new ObjectMapperDeserializationContextImpl()
ctx.type = cls
ctx.charset = findCharset()
ctx.contentType = contentType()
ctx.dataToDeserialize = new DataToDeserialize() {
@Override
String asString() {
return RestAssuredResponseOptionsGroovyImpl.this.asString()
}
@Override
byte[] asByteArray() {
return RestAssuredResponseOptionsGroovyImpl.this.asByteArray()
}
@Override
InputStream asInputStream() {
return RestAssuredResponseOptionsGroovyImpl.this.asInputStream()
}
}
ctx
}
}