Migrated to default WireMock helper methods; fixes gh-540

This commit is contained in:
Marcin Grzejszczak
2018-08-24 15:10:55 +02:00
parent a0bc2eb1c4
commit debea6edfd
15 changed files with 827 additions and 162 deletions

View File

@@ -1,89 +0,0 @@
package org.springframework.cloud.contract.spec
/**
* Contract for defining templated responses.
*
* If no implementation is provided then Handlebars will be picked as a default implementation.
*
* @author Marcin Grzejszczak
* @since 1.1.0
*/
interface ContractTemplate {
/**
* Handlebars is using the Mustache template thus it looks like this
* {{{ Mustache }}}. In this case the opening template would return {{{
*/
String openingTemplate()
/**
* Handlebars is using the Mustache template thus it looks like this
* {{{ Mustache }}}. In this case the closing template would return }}}
*/
String closingTemplate()
/**
* Returns the template for retrieving a URL path and query from request
*/
String url()
/**
* Returns the template for retrieving first value of a query parameter e.g. {{{ request.query.search }}}
* @param key
*/
String query(String key)
/**
* Returns the template for retrieving nth value of a query parameter (zero indexed) e.g. {{{ request.query.search.[5] }}}
* @param key
* @param index
*/
String query(String key, int index)
/**
* Returns the template for retrieving a URL path
*/
String path()
/**
* Returns the template for retrieving nth value of a URL path (zero indexed) e.g. {{{ request.path.[2] }}}
* @param index
*/
String path(int index)
/**
* Returns the template for retrieving the first value of a request header e.g. {{{ request.headers.X-Request-Id }}}
* @param key
*/
String header(String key)
/**
* Returns the template for retrieving the nth value of a request header (zero indexed) e.g. {{{ request.headers.X-Request-Id.[5] }}}
* @param key
* @param index
*/
String header(String key, int index)
/**
* Retruns the tempalte for retrieving the first value of a cookie with certain key
* @param key
*/
String cookie(String key)
/**
* Request body text (avoid for non-text bodies) e.g. {{{ request.body }}} . The body will not be escaped
* so you won't be able to directly embed it in a JSON for example.
*/
String body()
/**
* Request body text (avoid for non-text bodies) e.g. {{{ escapejsonbody }}} . The body will not be escaped
* so you will be able to embed it
*/
String escapedBody()
/**
* Request body text for the given JsonPath. e.g. {{{ jsonpath this '$.a.b.c' }}}
*/
String body(String jsonPath)
}

View File

@@ -0,0 +1,204 @@
package org.springframework.cloud.contract.spec;
/**
* Contract for defining templated responses.
* <p>
* If no implementation is provided then Handlebars will be picked as a default implementation.
*
* @author Marcin Grzejszczak
* @since 1.1.0
*/
public interface ContractTemplate {
/**
* Asserts whether the given text starts with proper opening template
*/
default boolean startsWithTemplate(String text) {
return text.startsWith(openingTemplate());
}
/**
* Asserts whether the given text starts with proper opening template for escaped value
*/
default boolean startsWithEscapedTemplate(String text) {
return text.startsWith(escapedOpeningTemplate());
}
/**
* Handlebars is using the Mustache template thus it looks like this
* {{ Mustache }}. In this case the opening template would return {{
*/
String openingTemplate();
/**
* Handlebars is using the Mustache template thus it looks like this
* {{ Mustache }}. In this case the closing template would return }}
*/
String closingTemplate();
/**
* Handlebars is using the Mustache template thus it looks like this
* {{{ Mustache }}}. In this case the opening template would return {{{
*/
default String escapedOpeningTemplate() {
return openingTemplate();
}
/**
* Handlebars is using the Mustache template thus it looks like this
* {{{ Mustache }}}. In this case the closing template would return }}}
*/
default String escapedClosingTemplate() {
return closingTemplate();
}
/**
* Returns the template for retrieving a URL path and query from request
*/
String url();
/**
* Returns the template for retrieving first value of a query parameter e.g. {{ request.query.search }}
*
* @param key
*/
String query(String key);
/**
* Returns the template for retrieving nth value of a query parameter (zero indexed) e.g. {{ request.query.search.[5] }}
*
* @param key
* @param index
*/
String query(String key, int index);
/**
* Returns the template for retrieving a URL path
*/
String path();
/**
* Returns the template for retrieving nth value of a URL path (zero indexed) e.g. {{ request.path.[2] }}
*
* @param index
*/
String path(int index);
/**
* Returns the template for retrieving the first value of a request header e.g. {{ request.headers.X-Request-Id }}
*
* @param key
*/
String header(String key);
/**
* Returns the template for retrieving the nth value of a request header (zero indexed) e.g. {{ request.headers.X-Request-Id.[5] }}
*
* @param key
* @param index
*/
String header(String key, int index);
/**
* Retruns the template for retrieving the first value of a cookie with certain key
*
* @param key
*/
String cookie(String key);
/**
* Request body text (avoid for non-text bodies) e.g. {{ request.body }} . The body will not be escaped
* so you won't be able to directly embed it in a JSON for example.
*/
String body();
/**
* Request body text for the given JsonPath. e.g. {{ jsonPath request.body '$.a.b.c' }}
*/
String body(String jsonPath);
/**
* Returns the template for retrieving a escaped URL path and query from request
*/
default String escapedUrl() {
return url();
}
/**
* Returns the template for retrieving escaped first value of a query parameter e.g. {{{ request.query.search }}}
*
* @param key
*/
default String escapedQuery(String key) {
return query(key);
}
/**
* Returns the template for retrieving esacped nth value of a query parameter (zero indexed) e.g. {{{ request.query.search.[5] }}}
*
* @param key
* @param index
*/
default String escapedQuery(String key, int index) {
return query(key, index);
}
/**
* Returns the template for retrieving a escaped URL path
*/
default String escapedPath() {
return path();
}
/**
* Returns the template for retrieving escaped nth value of a URL path (zero indexed) e.g. {{{ request.path.[2] }}}
*
* @param index
*/
default String escapedPath(int index) {
return path(index);
}
/**
* Returns the template for retrieving the escaped first value of a request header e.g. {{{ request.headers.X-Request-Id }}}
*
* @param key
*/
default String escapedHeader(String key) {
return header(key);
}
/**
* Returns the template for retrieving the esacaped nth value of a request header (zero indexed) e.g. {{{ request.headers.X-Request-Id.[5] }}}
*
* @param key
* @param index
*/
default String escapedHeader(String key, int index) {
return header(key, index);
}
/**
* Retruns the template for retrieving the escaped first value of a cookie with certain key
*
* @param key
*/
default String escapedCookie(String key) {
return cookie(key);
}
/**
* Request body text (avoid for non-text bodies) e.g. {{{ request.body }}} . The body will not be escaped
* so you won't be able to directly embed it in a JSON for example.
*/
default String escapedBody() {
return body();
}
/**
* Request body text for the given JsonPath. e.g. {{{ jsonPath request.body '$.a.b.c' }}}
*/
default String escapedBody(String jsonPath) {
return body(jsonPath);
}
}

View File

@@ -0,0 +1,155 @@
package org.springframework.cloud.contract.spec.internal
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.ContractTemplate
/**
* For backward compatibility when assertions take place,
* first checks the custom setup. Writes in a new format, can read
* the old format.
*
* @author Marcin Grzejszczak
* @since 2.1.0
*/
@CompileStatic
class CompositeContractTemplate implements ContractTemplate {
private final CustomHandlebarsContractTemplate custom = new CustomHandlebarsContractTemplate()
private final HandlebarsContractTemplate template = new HandlebarsContractTemplate()
@Override
boolean startsWithTemplate(String text) {
if (this.custom.startsWithTemplate(text)) {
return true
}
return template.startsWithTemplate(text)
}
@Override
boolean startsWithEscapedTemplate(String text) {
return this.template.startsWithEscapedTemplate(text)
}
@Override
String openingTemplate() {
return this.template.openingTemplate()
}
@Override
String closingTemplate() {
return this.template.closingTemplate()
}
@Override
String escapedOpeningTemplate() {
return this.template.escapedOpeningTemplate()
}
@Override
String escapedClosingTemplate() {
return this.template.escapedClosingTemplate()
}
@Override
String url() {
return this.template.url()
}
@Override
String query(String key) {
return this.template.query(key)
}
@Override
String query(String key, int index) {
return this.template.query(key, index)
}
@Override
String path() {
return this.template.path()
}
@Override
String path(int index) {
return this.template.path(index)
}
@Override
String header(String key) {
return this.template.header(key)
}
@Override
String header(String key, int index) {
return this.template.header(key, index)
}
@Override
String cookie(String key) {
return this.template.cookie(key)
}
@Override
String body() {
return this.template.body()
}
@Override
String escapedBody() {
// WireMock doesn't support proper escaping of JSON body
// that's why we need to use our custom handlebars extension
return this.custom.escapedBody()
}
@Override
String escapedBody(String jsonPath) {
return this.template.escapedBody(jsonPath)
}
@Override
String body(String jsonPath) {
return this.template.body(jsonPath)
}
@Override
String escapedUrl() {
return this.template.escapedUrl()
}
@Override
String escapedQuery(String key) {
return this.template.escapedQuery(key)
}
@Override
String escapedQuery(String key, int index) {
return this.template.escapedQuery(key, index)
}
@Override
String escapedPath() {
return this.template.escapedPath()
}
@Override
String escapedPath(int index) {
return this.template.escapedPath(index)
}
@Override
String escapedHeader(String key) {
return this.template.escapedHeader(key)
}
@Override
String escapedHeader(String key, int index) {
return this.template.escapedHeader(key, index)
}
@Override
String escapedCookie(String key) {
return this.template.escapedCookie(key)
}
}

View File

@@ -0,0 +1,94 @@
package org.springframework.cloud.contract.spec.internal
import groovy.transform.CompileStatic
import groovy.transform.PackageScope
import org.springframework.cloud.contract.spec.ContractTemplate
/**
* Represents the structure of templates using Handlebars compatible with
* WireMock template model requirements.
*
* @author Marcin Grzejszczak
* @since 1.1.0
*
* @deprecated use {@link HandlebarsContractTemplate}
*/
@CompileStatic
@PackageScope
class CustomHandlebarsContractTemplate implements ContractTemplate {
@Override
String openingTemplate() {
return "{{{"
}
@Override
String closingTemplate() {
return "}}}"
}
@Override
String url() {
return wrapped("request.url")
}
@Override
String query(String key) {
return query(key, 0)
}
@Override
String query(String key, int index) {
return wrapped("request.query.${key}.[${index}]")
}
@Override
String path() {
return wrapped("request.path")
}
@Override
String path(int index) {
return wrapped("request.path.[${index}]")
}
@Override
String header(String key) {
return header(key, 0)
}
@Override
String header(String key, int index) {
return wrapped("request.headers.${key}.[${index}]")
}
@Override
String cookie(String key) {
return wrapped("request.cookies.${key}")
}
@Override
String body() {
return wrapped("request.body")
}
@Override
String escapedBody() {
return wrapped("escapejsonbody")
}
@Override
String escapedBody(String jsonPath) {
return body(jsonPath)
}
@Override
String body(String jsonPath) {
return wrapped("jsonpath this '${jsonPath}'")
}
private String wrapped(String text) {
return openingTemplate() + text + closingTemplate()
}
}

View File

@@ -19,14 +19,14 @@ class FromRequest {
}
private ContractTemplate template() {
return new HandlebarsContractTemplate()
return new CompositeContractTemplate()
}
/**
* URL path and query
*/
DslProperty url() {
return new DslProperty(template.url())
return new DslProperty(template.escapedUrl())
}
/**
@@ -34,7 +34,7 @@ class FromRequest {
* @param key
*/
DslProperty query(String key) {
return new DslProperty(template.query(key))
return new DslProperty(template.escapedQuery(key))
}
/**
@@ -43,14 +43,14 @@ class FromRequest {
* @param index
*/
DslProperty query(String key, int index) {
return new DslProperty(template.query(key, index))
return new DslProperty(template.escapedQuery(key, index))
}
/**
* URL path
*/
DslProperty path() {
return new DslProperty(template.path())
return new DslProperty(template.escapedPath())
}
/**
@@ -58,7 +58,7 @@ class FromRequest {
* @param index
*/
DslProperty path(int index) {
return new DslProperty(template.path(index))
return new DslProperty(template.escapedPath(index))
}
/**
@@ -66,7 +66,7 @@ class FromRequest {
* @param key
*/
DslProperty header(String key) {
return new DslProperty(template.header(key))
return new DslProperty(template.escapedHeader(key))
}
/**
@@ -74,7 +74,7 @@ class FromRequest {
* @param key
*/
DslProperty header(String key, int index) {
return new DslProperty(template.header(key, index))
return new DslProperty(template.escapedHeader(key, index))
}
/**
@@ -82,20 +82,97 @@ class FromRequest {
* @param key
*/
DslProperty cookie(String key) {
return new DslProperty(template.cookie(key))
return new DslProperty(template.escapedCookie(key))
}
/**
* Request body text (avoid for non-text bodies)
*/
DslProperty body() {
return new DslProperty(template.body())
return new DslProperty(template.escapedBody())
}
/**
* Request body text for the given JsonPath
*/
DslProperty body(String jsonPath) {
return new DslProperty(template.escapedBody(jsonPath))
}
/**
* Unescaped URL path and query
*/
DslProperty rawUrl() {
return new DslProperty(template.url())
}
/**
* Unescaped First value of a query parameter e.g. request.query.search
* @param key
*/
DslProperty rawQuery(String key) {
return new DslProperty(template.query(key))
}
/**
* Unescaped nth value of a query parameter (zero indexed) e.g. request.query.search.[5]
* @param key
* @param index
*/
DslProperty rawQuery(String key, int index) {
return new DslProperty(template.query(key, index))
}
/**
* Unescaped URL path
*/
DslProperty rawPath() {
return new DslProperty(template.path())
}
/**
* Unescaped nth value of a URL path (zero indexed) e.g. {{{ request.path.[2] }}}
* @param index
*/
DslProperty rawPath(int index) {
return new DslProperty(template.path(index))
}
/**
* Unescaped First value of a request header e.g. request.headers.X-Request-Id
* @param key
*/
DslProperty rawHeader(String key) {
return new DslProperty(template.header(key))
}
/**
* Unescaped nth value of a request header (zero indexed) e.g. request.headers.X-Request-Id
* @param key
*/
DslProperty rawHeader(String key, int index) {
return new DslProperty(template.header(key, index))
}
/**
* Unescaped Retruns the tempalte for retrieving the first value of a cookie with certain key
* @param key
*/
DslProperty rawCookie(String key) {
return new DslProperty(template.cookie(key))
}
/**
* Unescaped Request body text (avoid for non-text bodies)
*/
DslProperty rawBody() {
return new DslProperty(template.body())
}
/**
* Unescaped Request body text for the given JsonPath
*/
DslProperty rawBody(String jsonPath) {
return new DslProperty(template.body(jsonPath))
}

View File

@@ -15,11 +15,21 @@ class HandlebarsContractTemplate implements ContractTemplate {
@Override
String openingTemplate() {
return "{{{"
return "{{"
}
@Override
String closingTemplate() {
return "}}"
}
@Override
String escapedOpeningTemplate() {
return "{{{"
}
@Override
String escapedClosingTemplate() {
return "}}}"
}
@@ -70,15 +80,64 @@ class HandlebarsContractTemplate implements ContractTemplate {
@Override
String escapedBody() {
return wrapped("escapejsonbody")
return escapedWrapped("request.body")
}
@Override
String escapedBody(String jsonPath) {
return escapedWrapped("jsonPath request.body '${jsonPath}'")
}
@Override
String body(String jsonPath) {
return wrapped("jsonpath this '${jsonPath}'")
return wrapped("jsonPath request.body '${jsonPath}'")
}
@Override
String escapedUrl() {
return escapedWrapped("request.url")
}
@Override
String escapedQuery(String key) {
return escapedQuery(key, 0)
}
@Override
String escapedQuery(String key, int index) {
return escapedWrapped("request.query.${key}.[${index}]")
}
@Override
String escapedPath() {
return escapedWrapped("request.path")
}
@Override
String escapedPath(int index) {
return escapedWrapped("request.path.[${index}]")
}
@Override
String escapedHeader(String key) {
return escapedHeader(key, 0)
}
@Override
String escapedHeader(String key, int index) {
return escapedWrapped("request.headers.${key}.[${index}]")
}
@Override
String escapedCookie(String key) {
return escapedWrapped("request.cookies.${key}")
}
private String wrapped(String text) {
return openingTemplate() + text + closingTemplate()
}
private String escapedWrapped(String text) {
return escapedOpeningTemplate() + text + escapedClosingTemplate()
}
}