Rewriting to java (#1131)

* Rewritting to java

- rewritten a lot of Spring Cloud Contract Spec module to Java
- extracted all Groovy stuff to a separate package
- will now continue writing extension modules
- spring-cloud-contract-spec will be written purely in java
- spring-cloud-contract-spec-groovy will have Groovy extensions

fixes gh-1130
This commit is contained in:
Marcin Grzejszczak
2019-07-08 14:15:21 +02:00
committed by GitHub
parent 1e11e99dac
commit 6b9e377487
195 changed files with 9167 additions and 5336 deletions

View File

@@ -0,0 +1,372 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec;
import java.util.Objects;
import java.util.function.Consumer;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import org.springframework.cloud.contract.spec.internal.Input;
import org.springframework.cloud.contract.spec.internal.OutputMessage;
import org.springframework.cloud.contract.spec.internal.Request;
import org.springframework.cloud.contract.spec.internal.Response;
/**
* The definition of a Contract. Contains helper methods in Groovy left for backward
* compatibility reasons.
*
* @since 1.0.0
*/
public class Contract {
/**
* You can set the level of priority of this contract. If there are two contracts
* mapped for example to the same endpoint, then the one with greater priority should
* take precedence. A priority of 1 is highest and takes precedence over a priority of
* 2.
*/
private Integer priority;
/**
* The HTTP request part of the contract.
*/
private Request request;
/**
* The HTTP response part of the contract.
*/
private Response response;
/**
* The label by which you'll reference the contract on the message consumer side.
*/
private String label;
/**
* Description of a contract. May be used in the documentation generation.
*/
private String description;
/**
* Name of the generated test / stub. If not provided then the file name will be used.
* If you have multiple contracts in a single file and you don't provide this value
* then a prefix will be added to the file with the index number while iterating over
* the collection of contracts.
*
* Remember to have a unique name for every single contract. Otherwise you might
* generate tests that have two identical methods or you will override the stubs.
*/
private String name;
/**
* The input side of a messaging contract.
*/
private Input input;
/**
* The output side of a messaging contract.
*/
private OutputMessage outputMessage;
/**
* Whether the contract should be ignored or not.
*/
private boolean ignored;
public Contract() {
}
/**
* You can set the level of priority of this contract. If there are two contracts
* mapped for example to the same endpoint, then the one with greater priority should
* take precedence. A priority of 1 is highest and takes precedence over a priority of
* 2.
* @param priority the higher the value the lower the priority
*/
public void priority(int priority) {
this.priority = priority;
}
/**
* Name of the generated test / stub. If not provided then the file name will be used.
* If you have multiple contracts in a single file and you don't provide this value
* then a prefix will be added to the file with the index number while iterating over
* the collection of contracts.
*
* Remember to have a unique name for every single contract. Otherwise you might
* generate tests that have two identical methods or you will override the stubs.
* @param name name of the contract
*/
public void name(String name) {
this.name = name;
}
/**
* Label used by the messaging contracts to trigger a message on the consumer side.
* @param label - name of the label of a messaging contract to trigger
*/
public void label(String label) {
this.label = label;
}
/**
* Description text. Might be used to describe the usage scenario.
* @param description - value of the description
*/
public void description(String description) {
this.description = description;
}
public static void assertContract(Contract dsl) {
if (dsl.getRequest() != null) {
if (dsl.request.getUrl() == null && dsl.request.getUrlPath() == null) {
throw new IllegalStateException(
"URL is missing for HTTP contract(exclude = { TraceWebClientAutoConfiguration.class })");
}
if (dsl.request.getMethod() == null) {
throw new IllegalStateException("Method is missing for HTTP contract");
}
}
if (dsl.response != null) {
if (dsl.response.getStatus() == null) {
throw new IllegalStateException("Status is missing for HTTP contract");
}
}
// Can't assert messaging part cause Pact doesn't require destinations it seems
}
/**
* Point of entry to build a contract.
* @param consumer function to manipulate the contract
* @return manipulated contract
*/
public static Contract make(Consumer<Contract> consumer) {
Contract contract = new Contract();
consumer.accept(contract);
return contract;
}
/**
* Groovy point of entry to build a contract. Left for backward compatibility reasons.
* @param closure function to manipulate the contract
* @return manipulated contract
*/
public static Contract make(@DelegatesTo(Contract.class) Closure closure) {
Contract dsl = new Contract();
closure.setDelegate(dsl);
closure.call();
Contract.assertContract(dsl);
return dsl;
}
/**
* The HTTP request part of the contract.
* @param consumer function to manipulate the request
*/
public void request(Consumer<Request> consumer) {
this.request = new Request();
consumer.accept(this.request);
}
/**
* The HTTP response part of the contract.
* @param consumer function to manipulate the response
*/
public void response(Consumer<Response> consumer) {
this.response = new Response();
consumer.accept(this.response);
}
/**
* The input part of the contract.
* @param consumer function to manipulate the input
*/
public void input(Consumer<Input> consumer) {
this.input = new Input();
consumer.accept(this.input);
}
/**
* The output part of the contract.
* @param consumer function to manipulate the output message
*/
public void outputMessage(Consumer<OutputMessage> consumer) {
this.outputMessage = new OutputMessage();
consumer.accept(this.outputMessage);
}
/**
* The HTTP request part of the contract.
* @param consumer function to manipulate the request
*/
public void request(@DelegatesTo(Request.class) Closure consumer) {
this.request = new Request();
consumer.setDelegate(this.request);
consumer.call();
}
/**
* The HTTP response part of the contract.
* @param consumer function to manipulate the response
*/
public void response(@DelegatesTo(Response.class) Closure consumer) {
this.response = new Response();
consumer.setDelegate(this.response);
consumer.call();
}
/**
* The input part of the contract.
* @param consumer function to manipulate the input
*/
public void input(@DelegatesTo(Input.class) Closure consumer) {
this.input = new Input();
consumer.setDelegate(this.input);
consumer.call();
}
/**
* The output part of the contract.
* @param consumer function to manipulate the output message
*/
public void outputMessage(@DelegatesTo(OutputMessage.class) Closure consumer) {
this.outputMessage = new OutputMessage();
consumer.setDelegate(this.outputMessage);
consumer.call();
}
/**
* Whether the contract should be ignored or not.
*/
public void ignored() {
this.ignored = true;
}
public Integer getPriority() {
return priority;
}
public void setPriority(Integer priority) {
this.priority = priority;
}
public Request getRequest() {
return request;
}
public void setRequest(Request request) {
this.request = request;
}
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Input getInput() {
return input;
}
public void setInput(Input input) {
this.input = input;
}
public OutputMessage getOutputMessage() {
return outputMessage;
}
public void setOutputMessage(OutputMessage outputMessage) {
this.outputMessage = outputMessage;
}
public boolean getIgnored() {
return ignored;
}
public boolean isIgnored() {
return ignored;
}
public void setIgnored(boolean ignored) {
this.ignored = ignored;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Contract contract = (Contract) o;
return ignored == contract.ignored && Objects.equals(priority, contract.priority)
&& Objects.equals(request, contract.request)
&& Objects.equals(response, contract.response)
&& Objects.equals(label, contract.label)
&& Objects.equals(description, contract.description)
&& Objects.equals(name, contract.name)
&& Objects.equals(input, contract.input)
&& Objects.equals(outputMessage, contract.outputMessage);
}
@Override
public int hashCode() {
return Objects.hash(priority, request, response, label, description, name, input,
outputMessage, ignored);
}
@Override
public String toString() {
return "Contract{" + "\npriority=" + priority + ", \n\trequest=" + request
+ ", \n\tresponse=" + response + ", \n\tlabel='" + label + '\''
+ ", \n\tdescription='" + description + '\'' + ", \n\tname='" + name
+ '\'' + ", \n\tinput=" + input + ", \n\toutputMessage=" + outputMessage
+ ", \n\tignored=" + ignored + '}';
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec;
import java.io.File;
import java.util.Collection;
/**
* Converter to be used to convert FROM {@link File} TO {@link Contract} and from
* {@link Contract} to {@code T}.
*
* @param <T> - type to which we want to convert the contract
* @author Marcin Grzejszczak
* @since 1.1.0
*/
public interface ContractConverter<T> extends ContractStorer<T> {
/**
* Should this file be accepted by the converter. Can use the file extension to check
* if the conversion is possible.
* @param file - file to be considered for conversion
* @return - {@code true} if the given implementation can convert the file
*/
boolean isAccepted(File file);
/**
* Converts the given {@link File} to its {@link Contract} representation.
* @param file - file to convert
* @return - {@link Contract} representation of the file
*/
Collection<Contract> convertFrom(File file);
/**
* Converts the given {@link Contract} to a {@link T} representation.
* @param contract - the parsed contract
* @return - {@link T} the type to which we do the conversion
*/
T convertTo(Collection<Contract> contract);
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec;
import java.util.HashMap;
import java.util.Map;
/**
* Defines how to store converted contracts to a byte array representation that can be
* stored to drive.
*
* @param <T> contracts type
* @author Marcin Grzejszczak
* @since 2.1.0
*/
public interface ContractStorer<T> {
/**
* Stores the contracts as a map of filename and String.
* @param contracts - to convert
* @return mapping of filename to converted byte array representation of the contract
*/
default Map<String, byte[]> store(T contracts) {
Map<String, byte[]> map = new HashMap<>();
map.put(String.valueOf(Math.abs(hashCode())), contracts.toString().getBytes());
return map;
}
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright 2013-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
*
* https://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 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 {
/**
* @param text text to assert
* @return asserts whether the given text starts with proper opening template.
*/
default boolean startsWithTemplate(String text) {
return text.startsWith(openingTemplate());
}
/**
* @param text text to assert
* @return 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 {{
* @return opening template for a variable
*/
String openingTemplate();
/**
* Handlebars is using the Mustache template thus it looks like this {{ Mustache }}.
* In this case the closing template would return }}
* @return closing template for a variable
*/
String closingTemplate();
/**
* Handlebars is using the Mustache template thus it looks like this {{{ Mustache }}}.
* In this case the opening template would return {{{
* @return escaped opening template for a variable
*/
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 }}}
* @return escaped closing template for a variable
*/
default String escapedClosingTemplate() {
return closingTemplate();
}
/**
* @return returns the template for retrieving a URL path and query from request
*/
String url();
/**
* @param key query parameter key
* @return the template for retrieving first value of a query parameter e.g. {{
* request.query.search }}
*/
String query(String key);
/**
* @param key query parameter key
* @param index query parameter index
* @return the template for retrieving nth value of a query parameter (zero indexed)
* e.g. {{ request.query.search.[5] }}
*/
String query(String key, int index);
/**
* @return the template for retrieving a URL path
*/
String path();
/**
* @param index request path index
* @return the template for retrieving nth value of a URL path (zero indexed) e.g. {{
* request.path.[2] }}
*/
String path(int index);
/**
* @param key headers key
* @return the template for retrieving the first value of a request header e.g. {{
* request.headers.X-Request-Id }}
*/
String header(String key);
/**
* @param key headers key
* @param index headers index
* @return the template for retrieving the nth value of a request header (zero
* indexed) e.g. {{ request.headers.X-Request-Id.[5] }}
*/
String header(String key, int index);
/**
* @param key cookie key
* @return the template for retrieving the first value of a cookie with certain key
*/
String cookie(String key);
/**
* @return 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();
/**
* @param jsonPath json path value
* @return request body text for the given JsonPath. e.g. {{ jsonPath request.body
* '$.a.b.c' }}
*/
String body(String jsonPath);
/**
* @return the template for retrieving a escaped URL path and query from request
*/
default String escapedUrl() {
return url();
}
/**
* @param key query parameter key
* @return the template for retrieving escaped first value of a query parameter e.g.
* {{{ request.query.search }}}
*/
default String escapedQuery(String key) {
return query(key);
}
/**
* @param key query parameter key
* @param index query parameter index
* @return the template for retrieving esacped nth value of a query parameter (zero
* indexed) e.g. {{{ request.query.search.[5] }}}
*/
default String escapedQuery(String key, int index) {
return query(key, index);
}
/**
* @return the template for retrieving a escaped URL path
*/
default String escapedPath() {
return path();
}
/**
* @param index path index
* @return the template for retrieving escaped nth value of a URL path (zero indexed)
* e.g. {{{ request.path.[2] }}}
*/
default String escapedPath(int index) {
return path(index);
}
/**
* @param key headers key
* @return the template for retrieving the escaped first value of a request header
* e.g. {{{ request.headers.X-Request-Id }}}
*/
default String escapedHeader(String key) {
return header(key);
}
/**
* @param key headers key
* @param index headers index
* @return the template for retrieving the esacaped nth value of a request header
* (zero indexed) e.g. {{{ request.headers.X-Request-Id.[5] }}}
*/
default String escapedHeader(String key, int index) {
return header(key, index);
}
/**
* @param key cookie key
* @return the template for retrieving the escaped first value of a cookie with
* certain key
*/
default String escapedCookie(String key) {
return cookie(key);
}
/**
* @return 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();
}
/**
* @param jsonPath json path value
* @return 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,32 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec;
/**
* @author Jakub Kubrynski, codearte.io
*/
public class ContractVerifierException extends RuntimeException {
public ContractVerifierException(String message) {
super(message);
}
public ContractVerifierException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Represents a body of a request / response or a message.
*
* @since 1.0.0
*/
public class Body extends DslProperty {
public Body(Map<String, DslProperty> body) {
super(extractValue(body, ContractUtils.CLIENT_VALUE),
extractValue(body, ContractUtils.SERVER_VALUE));
}
public Body(List<DslProperty> bodyAsList) {
super(bodyAsList.stream().map(DslProperty::getClientValue)
.collect(Collectors.toList()),
bodyAsList.stream().map(DslProperty::getServerValue)
.collect(Collectors.toList()));
}
public Body(Object value) {
super(ContractUtils.CLIENT_VALUE.apply(value),
ContractUtils.SERVER_VALUE.apply(value));
}
public Body(Byte[] bodyAsValue) {
super(bodyAsValue);
}
public Body(Number bodyAsValue) {
super(bodyAsValue);
}
public Body(String bodyAsValue) {
super(bodyAsValue, bodyAsValue);
}
public Body(DslProperty bodyAsValue) {
super(bodyAsValue.getClientValue(), bodyAsValue.getServerValue());
}
public Body(Serializable serializable) {
super(serializable, serializable);
}
public Body(MatchingStrategy matchingStrategy) {
super(matchingStrategy, matchingStrategy);
}
private static Map<String, Object> extractValue(Map<String, DslProperty> body,
final Function valueProvider) {
final Map<String, Object> map = new LinkedHashMap<String, Object>();
body.forEach((key, value) -> map.put(key, valueProvider.apply(value)));
return map;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Matchers for the given path.
*
* @author Marcin Grzejszczak
* @since 1.0.3
*/
public interface BodyMatcher {
/**
* @return What kind of matching are we dealing with.
*/
MatchingType matchingType();
/**
* @return Path to the path. Example for JSON it will be JSON Path.
*/
String path();
/**
* @return Optional value that the given path should be checked against. If there is
* no value then presence will be checked only together with type check. Example if we
* expect a JSON Path path {@code $.a} to be matched. by type, the defined response
* body contained an integer but the actual one contained a string then the assertion
* should fail.
*/
Object value();
/**
* @return Min no of occurrence when matching by type. In all other cases it will be
* ignored.
*/
Integer minTypeOccurrence();
/**
* @return Max no of occurrence when matching by type. In all other cases it will be
* ignored.
*/
Integer maxTypeOccurrence();
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
/**
* Matching strategy of dynamic parts of the body.
*
* @author Marcin Grzejszczak
* @author Olga Maciaszek-Sharma
* @since 1.0.3
*/
public class BodyMatchers {
protected final List<BodyMatcher> matchers = new LinkedList<>();
private final RegexPatterns regexPatterns = new RegexPatterns();
public void jsonPath(String path, MatchingTypeValue matchingType) {
this.matchers.add(new PathBodyMatcher(path, matchingType));
}
/**
* Adds xPath matcher; even though same implementation as in
* {@link BodyMatchers#jsonPath(java.lang.String, org.springframework.cloud.contract.spec.internal.MatchingTypeValue)},
* added for logical coherence in xml.
* @param xPath the xPath used to find the element to match
* @param matchingTypeValue to match the element found by the xPath against
*/
public void xPath(String xPath, MatchingTypeValue matchingTypeValue) {
this.matchers.add(new PathBodyMatcher(xPath, matchingTypeValue));
}
/**
* @deprecated use{@link #matchers()}
* @return json path matchers
*/
@Deprecated
public List<BodyMatcher> jsonPathMatchers() {
return matchers();
}
public boolean hasMatchers() {
return !this.matchers.isEmpty();
}
public List<BodyMatcher> matchers() {
return this.matchers;
}
public MatchingTypeValue byDate() {
return new MatchingTypeValue(MatchingType.DATE, this.regexPatterns.isoDate());
}
public MatchingTypeValue byTime() {
return new MatchingTypeValue(MatchingType.TIME, this.regexPatterns.isoTime());
}
public MatchingTypeValue byTimestamp() {
return new MatchingTypeValue(MatchingType.TIMESTAMP,
this.regexPatterns.isoDateTime());
}
public RegexMatchingTypeValue byRegex(String regex) {
return byRegex(Pattern.compile(regex));
}
public RegexMatchingTypeValue byRegex(RegexProperty regex) {
assertNotNull(regex);
return new RegexMatchingTypeValue(MatchingType.REGEX, regex);
}
public RegexMatchingTypeValue byRegex(Pattern regex) {
assertNotNull(regex);
return new RegexMatchingTypeValue(MatchingType.REGEX, new RegexProperty(regex));
}
public MatchingTypeValue byEquality() {
return new MatchingTypeValue(MatchingType.EQUALITY);
}
private void assertNotNull(Object object) {
if (object == null) {
throw new IllegalStateException("Object can't be null!");
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BodyMatchers that = (BodyMatchers) o;
return Objects.equals(matchers, that.matchers);
}
@Override
public int hashCode() {
return Objects.hash(matchers);
}
@Override
public String toString() {
return "BodyMatchers{" + "\nmatchers=" + matchers + '}';
}
/**
* The output part of the contract.
* @param consumer function to manipulate the output message
* @return matching type
*/
public MatchingTypeValue byType(Consumer<MatchingTypeValueHolder> consumer) {
MatchingTypeValueHolder matchingTypeValue = new MatchingTypeValueHolder();
consumer.accept(matchingTypeValue);
return matchingTypeValue.matchingTypeValue;
}
/**
* The output part of the contract.
* @param consumer function to manipulate the output message
* @return matching type
*/
public MatchingTypeValue byType(
@DelegatesTo(MatchingTypeValueHolder.class) Closure consumer) {
MatchingTypeValueHolder matchingTypeValue = new MatchingTypeValueHolder();
consumer.setDelegate(matchingTypeValue);
consumer.call();
return matchingTypeValue.matchingTypeValue;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Some properties can contain dynamic values. If that's the case we need to know how to
* generate a concrete value for them.
*
* @author Marcin Grzejszczak
* @since 2.1.0
*/
public interface CanBeDynamic {
/**
* @return a generated, concrete value.
*/
Object generateConcreteValue();
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Represents a client side {@link DslProperty}.
*
* @since 1.0.0
*/
public class ClientDslProperty extends DslProperty {
public ClientDslProperty(Object singleValue) {
super(singleValue);
}
public ClientDslProperty(Object client, Object server) {
super(client, server);
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
class ClientInput extends Input {
ClientInput(Input request) {
super(request);
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
class ClientOutputMessage extends OutputMessage {
ClientOutputMessage(OutputMessage request) {
super(request);
}
}

View File

@@ -0,0 +1,402 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Contains useful common methods for the DSL.
*
* @since 1.0.0
*/
public class Common {
private final RegexPatterns regexPatterns = new RegexPatterns();
public Map<String, DslProperty> convertObjectsToDslProperties(
Map<String, Object> body) {
return body.entrySet().stream().collect(Collectors.toMap(
(Function<Map.Entry, String>) t -> t.getKey().toString(),
(Function<Map.Entry, DslProperty>) t -> toDslProperty(t.getValue()),
throwingMerger(), LinkedHashMap::new));
}
private static <T> BinaryOperator<T> throwingMerger() {
return (u, v) -> {
throw new IllegalStateException(String.format("Duplicate key %s", u));
};
}
public Collection convertObjectsToDslProperties(List<Object> body) {
return body.stream().map(this::toDslProperty).collect(Collectors.toList());
}
public DslProperty toDslProperty(Object property) {
return new DslProperty(property);
}
public DslProperty toDslProperty(Map property) {
return new DslProperty(convertObjectsToDslProperties(property));
}
public DslProperty toDslProperty(List property) {
return new DslProperty(convertObjectsToDslProperties(property));
}
public DslProperty toDslProperty(DslProperty property) {
return property;
}
public NamedProperty named(DslProperty name, DslProperty value) {
return new NamedProperty(name, value);
}
public NamedProperty named(DslProperty name, DslProperty value,
DslProperty contentType) {
return new NamedProperty(name, value, contentType);
}
public NamedProperty named(Map<String, DslProperty> namedMap) {
return new NamedProperty(namedMap);
}
public DslProperty value(DslProperty value) {
return value;
}
public DslProperty $(DslProperty value) {
return value;
}
public DslProperty value(Object value) {
return new DslProperty(value);
}
public DslProperty $(Object value) {
return new DslProperty(value);
}
public DslProperty value(ClientDslProperty client, ServerDslProperty server) {
assertThatSidesMatch(client.getClientValue(), server.getServerValue());
return new DslProperty(client.getClientValue(), server.getServerValue());
}
public DslProperty $(ClientDslProperty client, ServerDslProperty server) {
return value(client, server);
}
public DslProperty value(ServerDslProperty server, ClientDslProperty client) {
assertThatSidesMatch(client.getClientValue(), server.getServerValue());
return new DslProperty(client.getClientValue(), server.getServerValue());
}
public DslProperty $(ServerDslProperty server, ClientDslProperty client) {
return value(server, client);
}
public RegexProperty regex(String regex) {
return regexProperty(Pattern.compile(regex));
}
public RegexProperty regex(RegexProperty regex) {
return regex;
}
public RegexProperty regex(Pattern regex) {
return regexProperty(regex);
}
public OptionalProperty optional(Object object) {
return new OptionalProperty(object);
}
public RegexProperty regexProperty(Object object) {
return new RegexProperty(object);
}
public ExecutionProperty execute(String commandToExecute) {
return new ExecutionProperty(commandToExecute);
}
/**
* Helper method to provide a better name for the consumer side.
* @param clientValue client value
* @return client dsl property
*/
public ClientDslProperty client(Object clientValue) {
return new ClientDslProperty(clientValue);
}
/**
* Helper method to provide a better name for the consumer side.
* @param clientValue client value
* @return client dsl property
*/
public ClientDslProperty stub(Object clientValue) {
return new ClientDslProperty(clientValue);
}
/**
* Helper method to provide a better name for the consumer side.
* @param clientValue client value
* @return client dsl property
*/
public ClientDslProperty consumer(Object clientValue) {
return new ClientDslProperty(clientValue);
}
/**
* Helper method to provide a better name for the server side.
* @param serverValue server value
* @return server dsl property
*/
public ServerDslProperty server(Object serverValue) {
return new ServerDslProperty(serverValue);
}
/**
* Helper method to provide a better name for the consumer side.
* @param clientValue client value
* @return client dsl property
*/
public ClientDslProperty c(Object clientValue) {
return new ClientDslProperty(clientValue);
}
/**
* Helper method to provide a better name for the server side.
* @param serverValue server value
* @return server dsl property
*/
public ServerDslProperty p(Object serverValue) {
return new ServerDslProperty(serverValue);
}
/**
* Helper method to provide a better name for the server side.
* @param serverValue server value
* @return server dsl property
*/
public ServerDslProperty test(Object serverValue) {
return new ServerDslProperty(serverValue);
}
/**
* Read file contents as String.
* @param relativePath of the file to read
* @return String file contents
*/
public FromFileProperty file(String relativePath) {
return file(relativePath, Charset.defaultCharset());
}
/**
* Read file contents as bytes[].
* @param relativePath of the file to read
* @return String file contents
*/
public FromFileProperty fileAsBytes(String relativePath) {
return new FromFileProperty(fileLocation(relativePath), byte[].class);
}
/**
* Read file contents as String with the given Charset.
* @param relativePath of the file to read
* @param charset to use for converting the bytes to String
* @return String file contents
*/
public FromFileProperty file(String relativePath, Charset charset) {
return new FromFileProperty(fileLocation(relativePath), String.class, charset);
}
/**
* Read file contents as array of bytes.
* @param relativePath of the file to read
* @return file contents as an array of bytes
*/
private File fileLocation(String relativePath) {
URL resource = Thread.currentThread().getContextClassLoader()
.getResource(relativePath);
if (resource == null) {
throw new IllegalStateException("File [" + relativePath + "] is not present");
}
try {
return new File(resource.toURI());
}
catch (URISyntaxException ex) {
throw new IllegalStateException(ex);
}
}
/**
* Helper method to provide a better name for the server side.
* @param serverValue server value
* @return server dsl property
*/
public ServerDslProperty producer(Object serverValue) {
return new ServerDslProperty(serverValue);
}
private void throwAbsentError() {
throw new IllegalStateException("Absent cannot only be used only on one side");
}
private void assertThat(boolean condition, String msg) {
if (!condition) {
throw new IllegalStateException(msg);
}
}
public void assertThatSidesMatch(Object firstSide, Object secondSide) {
if (firstSide instanceof OptionalProperty) {
assertThat(
secondSide.toString()
.matches(((OptionalProperty) firstSide).optionalPattern()),
"Pattern [" + ((OptionalProperty) firstSide).optionalPattern()
+ "] is not matched by [" + secondSide.toString() + "]");
}
else if ((firstSide instanceof Pattern || firstSide instanceof RegexProperty)
&& secondSide instanceof String) {
Pattern pattern = firstSide instanceof Pattern ? (Pattern) firstSide
: ((RegexProperty) firstSide).getPattern();
assertThat(((String) secondSide).toString().matches(pattern.pattern()),
"Pattern [" + pattern.pattern() + "] is not matched by ["
+ secondSide.toString() + "]");
}
else if ((secondSide instanceof Pattern || secondSide instanceof RegexProperty)
&& firstSide instanceof String) {
Pattern pattern = secondSide instanceof Pattern ? (Pattern) secondSide
: ((RegexProperty) secondSide).getPattern();
assertThat(((String) firstSide).matches(pattern.pattern()),
"Pattern [" + pattern.pattern() + "] is not matched by ["
+ firstSide.toString() + "]");
}
else if (firstSide instanceof MatchingStrategy
&& secondSide instanceof MatchingStrategy) {
if (((MatchingStrategy) firstSide).getType()
.equals(MatchingStrategy.Type.ABSENT)
&& !((MatchingStrategy) secondSide).getType()
.equals(MatchingStrategy.Type.ABSENT)) {
throwAbsentError();
}
}
else if (firstSide instanceof MatchingStrategy) {
if (((MatchingStrategy) firstSide).getType()
.equals(MatchingStrategy.Type.ABSENT)) {
throwAbsentError();
}
}
else if (secondSide instanceof MatchingStrategy) {
if (((MatchingStrategy) secondSide).getType()
.equals(MatchingStrategy.Type.ABSENT)) {
throwAbsentError();
}
}
}
public RegexProperty onlyAlphaUnicode() {
return regexPatterns.onlyAlphaUnicode();
}
public RegexProperty alphaNumeric() {
return regexPatterns.alphaNumeric();
}
public RegexProperty number() {
return regexPatterns.number();
}
public RegexProperty positiveInt() {
return regexPatterns.positiveInt();
}
public RegexProperty anyBoolean() {
return regexPatterns.anyBoolean();
}
public RegexProperty anInteger() {
return regexPatterns.anInteger();
}
public RegexProperty aDouble() {
return regexPatterns.aDouble();
}
public RegexProperty ipAddress() {
return regexPatterns.ipAddress();
}
public RegexProperty hostname() {
return regexPatterns.hostname();
}
public RegexProperty email() {
return regexPatterns.email();
}
public RegexProperty url() {
return regexPatterns.url();
}
public RegexProperty httpsUrl() {
return regexPatterns.httpsUrl();
}
public RegexProperty uuid() {
return regexPatterns.uuid();
}
public RegexProperty isoDate() {
return regexPatterns.isoDate();
}
public RegexProperty isoDateTime() {
return regexPatterns.isoDateTime();
}
public RegexProperty isoTime() {
return regexPatterns.isoTime();
}
public RegexProperty iso8601WithOffset() {
return regexPatterns.iso8601WithOffset();
}
public RegexProperty nonEmpty() {
return regexPatterns.nonEmpty();
}
public RegexProperty nonBlank() {
return regexPatterns.nonBlank();
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
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
*/
public class CompositeContractTemplate implements ContractTemplate {
private final CustomHandlebarsContractTemplate custom = new CustomHandlebarsContractTemplate();
private final HandlebarsContractTemplate template = new HandlebarsContractTemplate();
@Override
public boolean startsWithTemplate(String text) {
if (this.custom.startsWithTemplate(text)) {
return true;
}
return template.startsWithTemplate(text);
}
@Override
public boolean startsWithEscapedTemplate(String text) {
return this.template.startsWithEscapedTemplate(text);
}
@Override
public String openingTemplate() {
return this.template.openingTemplate();
}
@Override
public String closingTemplate() {
return this.template.closingTemplate();
}
@Override
public String escapedOpeningTemplate() {
return this.template.escapedOpeningTemplate();
}
@Override
public String escapedClosingTemplate() {
return this.template.escapedClosingTemplate();
}
@Override
public String url() {
return this.template.url();
}
@Override
public String query(String key) {
return this.template.query(key);
}
@Override
public String query(String key, int index) {
return this.template.query(key, index);
}
@Override
public String path() {
return this.template.path();
}
@Override
public String path(int index) {
return this.template.path(index);
}
@Override
public String header(String key) {
return this.template.header(key);
}
@Override
public String header(String key, int index) {
return this.template.header(key, index);
}
@Override
public String cookie(String key) {
return this.template.cookie(key);
}
@Override
public String body() {
return this.template.body();
}
@Override
public 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
public String escapedBody(String jsonPath) {
return this.template.escapedBody(jsonPath);
}
@Override
public String body(String jsonPath) {
return this.template.body(jsonPath);
}
@Override
public String escapedUrl() {
return this.template.escapedUrl();
}
@Override
public String escapedQuery(String key) {
return this.template.escapedQuery(key);
}
@Override
public String escapedQuery(String key, int index) {
return this.template.escapedQuery(key, index);
}
@Override
public String escapedPath() {
return this.template.escapedPath();
}
@Override
public String escapedPath(int index) {
return this.template.escapedPath(index);
}
@Override
public String escapedHeader(String key) {
return this.template.escapedHeader(key);
}
@Override
public String escapedHeader(String key, int index) {
return this.template.escapedHeader(key, index);
}
@Override
public String escapedCookie(String key) {
return this.template.escapedCookie(key);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.function.Function;
final class ContractUtils {
static final Function CLIENT_VALUE = o -> o instanceof DslProperty
? ((DslProperty) o).getClientValue() : o;
static final Function SERVER_VALUE = o -> o instanceof DslProperty
? ((DslProperty) o).getServerValue() : o;
private ContractUtils() {
throw new IllegalStateException("Can't instantiate an utility class");
}
static Object convertStubSideRecursively(Object object) {
return convertRecursively(object, CLIENT_VALUE);
}
static Object convertTestSideRecursively(Object object) {
return convertRecursively(object, SERVER_VALUE);
}
private static Object convertRecursively(Object object, Function function) {
if (object instanceof DslProperty) {
return convertRecursively(function.apply(object), function);
}
return object;
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
/**
* Represents a http cookie.
*
* @author Alex Xandra Albert Sim
* @since 1.2.5
*/
public class Cookie extends DslProperty {
private String key;
public Cookie(String key, DslProperty dslProperty) {
super(dslProperty.getClientValue(), dslProperty.getServerValue());
this.key = key;
}
public Cookie(String key, MatchingStrategy value) {
super(value);
this.key = key;
}
public Cookie(String key, Object value) {
super(ContractUtils.CLIENT_VALUE.apply(value),
ContractUtils.SERVER_VALUE.apply(value));
this.key = key;
}
public static Cookie build(String key, Object value) {
if (value instanceof MatchingStrategy) {
return new Cookie(key, (MatchingStrategy) value);
}
return new Cookie(key, value);
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
Cookie cookie = (Cookie) o;
return Objects.equals(key, cookie.key);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), key);
}
@Override
public String toString() {
return "Cookie{" + "key='" + key + '\'' + "}, value=" + super.toString();
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
/**
* Represents a set of http cookies.
*
* @author Alex Xandra Albert Sim
* @since 1.2.5
*/
public class Cookies {
private Set<Cookie> entries = new HashSet<>();
public void cookie(Map<String, Object> singleCookie) {
Iterator<Map.Entry<String, Object>> iterator = singleCookie.entrySet().iterator();
if (iterator.hasNext()) {
Map.Entry<String, Object> first = iterator.next();
if (first != null) {
entries.add(Cookie.build(first.getKey(), first.getValue()));
}
}
}
public void cookie(String cookieKey, Object cookieValue) {
entries.add(Cookie.build(cookieKey, cookieValue));
}
public void executeForEachCookie(final Consumer<Cookie> consumer) {
entries.forEach(consumer::accept);
}
public DslProperty matching(String value) {
return new DslProperty(value);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Cookies cookies = (Cookies) o;
return Objects.equals(entries, cookies.entries);
}
@Override
public int hashCode() {
return Objects.hash(entries);
}
@Override
public String toString() {
return "Cookies{" + "entries=" + entries + '}';
}
/**
* Converts the headers into their stub side representations and returns as a map of
* String key => Object value.
* @return converted map
*/
public Map<String, Object> asStubSideMap() {
final Map<String, Object> map = new LinkedHashMap<>();
entries.forEach(cookie -> map.put(cookie.getKey(),
ContractUtils.convertStubSideRecursively(cookie)));
return map;
}
/**
* Converts the headers into their stub side representations and returns as a map of
* String key => Object value.
* @return converted map
*/
public Map<String, Object> asTestSideMap() {
final Map<String, Object> map = new HashMap<String, Object>();
entries.forEach(cookie -> map.put(cookie.getKey(),
ContractUtils.convertTestSideRecursively(cookie)));
return map;
}
public Set<Cookie> getEntries() {
return entries;
}
public void setEntries(Set<Cookie> entries) {
this.entries = entries;
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
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}
*/
class CustomHandlebarsContractTemplate implements ContractTemplate {
@Override
public String openingTemplate() {
return "{{{";
}
@Override
public String closingTemplate() {
return "}}}";
}
@Override
public String url() {
return wrapped("request.url");
}
@Override
public String query(String key) {
return query(key, 0);
}
@Override
public String query(String key, int index) {
return wrapped("request.query." + key + ".[" + index + "]");
}
@Override
public String path() {
return wrapped("request.path");
}
@Override
public String path(int index) {
return wrapped("request.path.[" + index + "]");
}
@Override
public String header(String key) {
return header(key, 0);
}
@Override
public String header(String key, int index) {
return wrapped("request.headers." + key + ".[" + index + "]");
}
@Override
public String cookie(String key) {
return wrapped("request.cookies." + key);
}
@Override
public String body() {
return wrapped("request.body");
}
@Override
public String escapedBody() {
return wrapped("escapejsonbody");
}
@Override
public String escapedBody(String jsonPath) {
return body(jsonPath);
}
@Override
public String body(String jsonPath) {
return wrapped("jsonpath this \'" + jsonPath + "\'");
}
private String wrapped(String text) {
return openingTemplate() + text + closingTemplate();
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.io.Serializable;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* Represents an element of a DSL that can contain client or sever side values.
*
* @since 1.0.0
*/
public class DslProperty<T> implements Serializable {
private final T clientValue;
private final T serverValue;
public DslProperty(T clientValue, T serverValue) {
this.clientValue = clientValue;
this.serverValue = serverValue;
}
public DslProperty(T singleValue) {
this.clientValue = singleValue;
this.serverValue = singleValue;
}
public boolean isSingleValue() {
return this.clientValue.equals(this.serverValue)
|| (this.clientValue != null && this.serverValue == null)
|| (this.serverValue != null && this.clientValue == null);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DslProperty<?> that = (DslProperty<?>) o;
Object thisClientValue = stringPatternIfPattern(clientValue);
Object thatClientValue = stringPatternIfPattern(that.clientValue);
Object thisServerValue = stringPatternIfPattern(serverValue);
Object thatServerValue = stringPatternIfPattern(that.serverValue);
return Objects.equals(thisClientValue, thatClientValue)
&& Objects.equals(thisServerValue, thatServerValue);
}
private Object stringPatternIfPattern(Object value) {
return value instanceof Pattern ? ((Pattern) value).pattern() : value;
}
@Override
public int hashCode() {
return Objects.hash(stringPatternIfPattern(clientValue),
stringPatternIfPattern(serverValue));
}
@Override
public String toString() {
return getClass().getSimpleName() + "{" + "\nclientValue=" + clientValue
+ ", \n\tserverValue=" + serverValue + '}';
}
public final T getClientValue() {
return clientValue;
}
public final T getServerValue() {
return serverValue;
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.List;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* A hook mechanism to allow external languages and frameworks to convert types that are
* not in Java for Spring Cloud Contract to understand.
*
* @author Marcin Grzejszczak
* @since 2.2.0
*/
public interface DslPropertyConverter {
/**
* Default no op implementation.
*/
DslPropertyConverter DEFAULT = new DslPropertyConverter() {
};
/**
* A composite over available instances or default if none is present.
*/
DslPropertyConverter INSTANCE = instance();
/**
* @return a composite {@link DslPropertyConverter} around a list of
* {@link DslPropertyConverter} or a default no op instance
*/
static DslPropertyConverter instance() {
if (INSTANCE != null) {
return INSTANCE;
}
List<DslPropertyConverter> converters = SpringFactoriesLoader
.loadFactories(DslPropertyConverter.class, null);
if (converters.isEmpty()) {
return DEFAULT;
}
return new DslPropertyConverter() {
@Override
public Object testSide(Object object) {
Object convertedObject = object;
for (DslPropertyConverter converter : converters) {
convertedObject = converter.testSide(convertedObject);
}
return convertedObject;
}
@Override
public Object stubSide(Object object) {
Object convertedObject = object;
for (DslPropertyConverter converter : converters) {
convertedObject = converter.stubSide(convertedObject);
}
return convertedObject;
}
};
}
/**
* Conversion mechanism for the test side manipulation.
* @param object object to manipulate
* @return converted object for the test side
*/
default Object testSide(Object object) {
return object;
}
/**
* Conversion mechanism for the stub side manipulation.
* @param object object to manipulate
* @return converted object for the stub side
*/
default Object stubSide(Object object) {
return object;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.io.Serializable;
import java.util.Objects;
/**
* Represents a property that will become an executable method in the generated tests.
*
* @since 1.0.0
*/
public class ExecutionProperty implements Serializable {
private static final String PLACEHOLDER_VALUE = "$it";
private final String executionCommand;
public ExecutionProperty(String executionCommand) {
this.executionCommand = executionCommand;
}
/**
* Inserts the provided code as a parameter to the method and returns the code that
* represents that method execution.
* @param valueToInsert value to insert into the string
* @return string with inserted value
*/
public String insertValue(String valueToInsert) {
return executionCommand.replace(PLACEHOLDER_VALUE, valueToInsert);
}
@Override
public String toString() {
return executionCommand;
}
public final String getExecutionCommand() {
return executionCommand;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ExecutionProperty that = (ExecutionProperty) o;
return Objects.equals(executionCommand, that.executionCommand);
}
@Override
public int hashCode() {
return Objects.hash(executionCommand);
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.file.Files;
/**
* Represents a property that will become a File content.
*
* @since 2.1.0
*/
public class FromFileProperty implements Serializable {
private final File file;
private final String charset;
private final Class type;
public FromFileProperty(File file, Class type) {
this(file, type, Charset.defaultCharset());
}
public FromFileProperty(File file, Class type, Charset charset) {
this.file = file;
this.type = type;
this.charset = charset.toString();
}
public boolean isString() {
return String.class.equals(this.type);
}
public boolean isByte() {
return byte[].class.equals(this.type) || Byte[].class.equals(this.type);
}
public String asString() {
try {
return new String(asBytes(), this.charset);
}
catch (UnsupportedEncodingException ex) {
throw new IllegalStateException(ex);
}
}
public String fileName() {
return this.file.getName();
}
public byte[] asBytes() {
try {
return Files.readAllBytes(this.file.toPath());
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
@Override
public String toString() {
return asString();
}
public final File getFile() {
return file;
}
public final String getCharset() {
return charset;
}
public final Class getType() {
return type;
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import org.springframework.cloud.contract.spec.ContractTemplate;
/**
* Helper class to reference the request body parameters.
*
* @author Marcin Grzejszczak
* @since 1.1.0
*/
public class FromRequest {
private final ContractTemplate template;
public FromRequest() {
this.template = template();
}
private ContractTemplate template() {
return new CompositeContractTemplate();
}
/**
* @return URL path and query.
*/
public DslProperty url() {
return new DslProperty(template.escapedUrl());
}
/**
* First value of a query parameter e.g. request.query.search.
* @param key key for the query param
* @return dsl property
*/
public DslProperty query(String key) {
return new DslProperty(template.escapedQuery(key));
}
/**
* nth value of a query parameter (zero indexed) e.g. request.query.search.[5].
* @param key key for the query param
* @param index index of the query param
* @return dsl property
*/
public DslProperty query(String key, int index) {
return new DslProperty(template.escapedQuery(key, index));
}
/**
* URL path.
* @return dsl property
*/
public DslProperty path() {
return new DslProperty(template.escapedPath());
}
/**
* nth value of a URL path (zero indexed) e.g. {{{ request.path.[2] }}}* @param index.
* @param index path index
* @return dsl property
*/
public DslProperty path(int index) {
return new DslProperty(template.escapedPath(index));
}
/**
* First value of a request header e.g. request.headers.X-Request-Id.
* @param key header key
* @return dsl property
*/
public DslProperty header(String key) {
return new DslProperty(template.escapedHeader(key));
}
/**
* nth value of a request header (zero indexed) e.g. request.headers.X-Request-Id.
* @param key header key
* @param index header index
* @return dsl property
*/
public DslProperty header(String key, int index) {
return new DslProperty(template.escapedHeader(key, index));
}
/**
* Retruns the tempalte for retrieving the first value of a cookie with certain key.
* @param key cookie key
* @return dsl property
*/
public DslProperty cookie(String key) {
return new DslProperty(template.escapedCookie(key));
}
/**
* Request body text (avoid for non-text bodies).
* @return dsl property
*/
public DslProperty body() {
return new DslProperty(template.escapedBody());
}
/**
* Request body text for the given JsonPath.
* @param jsonPath json path body
* @return dsl property
*/
public DslProperty body(String jsonPath) {
return new DslProperty(template.escapedBody(jsonPath));
}
/**
* Unescaped URL path and query.
* @return dsl property
*/
public DslProperty rawUrl() {
return new DslProperty(template.url());
}
/**
* Unescaped First value of a query parameter e.g. request.query.search.
* @param key query key
* @return dsl property
*/
public 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 query key
* @param index query index
* @return dsl property
*/
public DslProperty rawQuery(String key, int index) {
return new DslProperty(template.query(key, index));
}
/**
* Unescaped URL path.
* @return dsl property
*/
public DslProperty rawPath() {
return new DslProperty(template.path());
}
/**
* Unescaped nth value of a URL path (zero indexed) e.g. {{{ request.path.[2]. }}}*
* @param index path index
* @return dsl property
*/
public 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 header key
* @return dsl property
*/
public 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 header key
* @param index header index
* @return dsl property
*/
public DslProperty rawHeader(String key, int index) {
return new DslProperty(template.header(key, index));
}
/**
* Unescaped Returns the template for retrieving the first value of a cookie with
* certain key.
* @param key cookie key
* @return dsl property
*/
public DslProperty rawCookie(String key) {
return new DslProperty(template.cookie(key));
}
/**
* Unescaped Request body text (avoid for non-text bodies).
* @return dsl property
*/
public DslProperty rawBody() {
return new DslProperty(template.body());
}
/**
* Unescaped Request body text for the given JsonPath.
* @param jsonPath json path body
* @return dsl property
*/
public DslProperty rawBody(String jsonPath) {
return new DslProperty(template.body(jsonPath));
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
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
*/
public class HandlebarsContractTemplate implements ContractTemplate {
@Override
public String openingTemplate() {
return "{{";
}
@Override
public String closingTemplate() {
return "}}";
}
@Override
public String escapedOpeningTemplate() {
return "{{{";
}
@Override
public String escapedClosingTemplate() {
return "}}}";
}
@Override
public String url() {
return wrapped("request.url");
}
@Override
public String query(String key) {
return query(key, 0);
}
@Override
public String query(String key, int index) {
return wrapped("request.query." + key + ".[" + String.valueOf(index) + "]");
}
@Override
public String path() {
return wrapped("request.path");
}
@Override
public String path(int index) {
return wrapped("request.path.[" + String.valueOf(index) + "]");
}
@Override
public String header(String key) {
return header(key, 0);
}
@Override
public String header(String key, int index) {
return wrapped("request.headers." + key + ".[" + String.valueOf(index) + "]");
}
@Override
public String cookie(String key) {
return wrapped("request.cookies." + key);
}
@Override
public String body() {
return wrapped("request.body");
}
@Override
public String escapedBody() {
return escapedWrapped("request.body");
}
@Override
public String escapedBody(String jsonPath) {
return escapedWrapped("jsonPath request.body \'" + jsonPath + "\'");
}
@Override
public String body(String jsonPath) {
return wrapped("jsonPath request.body \'" + jsonPath + "\'");
}
@Override
public String escapedUrl() {
return escapedWrapped("request.url");
}
@Override
public String escapedQuery(String key) {
return escapedQuery(key, 0);
}
@Override
public String escapedQuery(String key, int index) {
return escapedWrapped(
"request.query." + key + ".[" + String.valueOf(index) + "]");
}
@Override
public String escapedPath() {
return escapedWrapped("request.path");
}
@Override
public String escapedPath(int index) {
return escapedWrapped("request.path.[" + String.valueOf(index) + "]");
}
@Override
public String escapedHeader(String key) {
return escapedHeader(key, 0);
}
@Override
public String escapedHeader(String key, int index) {
return escapedWrapped(
"request.headers." + key + ".[" + String.valueOf(index) + "]");
}
@Override
public 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();
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
/**
* Represents a header of a request / response or a message.
*
* @since 1.0.0
*/
public class Header extends DslProperty {
private String name;
public Header(String name, DslProperty dslProperty) {
super(dslProperty.getClientValue(), dslProperty.getServerValue());
this.name = name;
}
public Header(String name, MatchingStrategy value) {
super(value);
this.name = name;
}
public Header(String name, Object value) {
super(ContractUtils.CLIENT_VALUE.apply(value),
ContractUtils.SERVER_VALUE.apply(value));
this.name = name;
}
public static Header build(String key, Object value) {
if (value instanceof MatchingStrategy) {
return new Header(key, (MatchingStrategy) value);
}
return new Header(key, value);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
Header header = (Header) o;
return Objects.equals(name, header.name);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), name);
}
@Override
public String toString() {
return "Header{" + "\nname='" + name + '\'' + "} \n" + super.toString();
}
}

View File

@@ -0,0 +1,490 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.regex.Pattern;
/**
* Represents a set of headers of a request / response or a message.
*
* @since 1.0.0
*/
public class Headers {
private static final BiFunction<String, Header, Object> CLIENT_SIDE = (s,
header) -> ContractUtils.convertStubSideRecursively(header);
private static final BiFunction<String, Header, Object> SERVER_SIDE = (s,
header) -> ContractUtils.convertTestSideRecursively(header);
private MediaTypes mediaTypes = new MediaTypes();
private HttpHeaders httpHeaders = new HttpHeaders();
private MessagingHeaders messagingHeaders = new MessagingHeaders();
private Set<Header> entries = new LinkedHashSet<>();
public void header(Map<String, Object> singleHeader) {
Iterator<Map.Entry<String, Object>> iterator = singleHeader.entrySet().iterator();
if (iterator.hasNext()) {
Map.Entry<String, Object> first = iterator.next();
if (first != null) {
entries.add(Header.build(first.getKey(), first.getValue()));
}
}
}
public void header(String headerKey, Object headerValue) {
entries.add(Header.build(headerKey, headerValue));
}
public void executeForEachHeader(final Consumer<Header> consumer) {
entries.forEach(consumer);
}
public void headers(Set<Header> headers) {
entries.addAll(headers);
}
public void accept(String contentType) {
header(accept(), matching(contentType));
}
public void contentType(String contentType) {
header(httpHeaders.contentType(), matching(contentType));
}
public void messagingContentType(String contentType) {
header(messagingHeaders.messagingContentType(), matching(contentType));
}
/**
* If for the consumer / producer you want to match exactly only the root of content
* type. I.e. {@code application/json;charset=UTF8} you care only about
* {@code application/json} then you should use this method
* @param value regex as String
* @return dsl property
*/
public DslProperty matching(String value) {
return new DslProperty(value);
}
protected NotToEscapePattern notEscaped(Pattern pattern) {
return new NotToEscapePattern(pattern);
}
public Map<String, Object> asMap(final BiFunction<String, Header, Object> consumer) {
final Map<String, Object> map = new LinkedHashMap<>();
entries.forEach(header -> map.put(header.getName(),
consumer.apply(header.getName(), header)));
return map;
}
/**
* Converts the headers into their stub side representations and returns as a map of
* String key => Object value.
* @return converted map
*/
public Map<String, Object> asStubSideMap() {
return asMap(CLIENT_SIDE);
}
/**
* Converts the headers into their stub side representations and returns as a map of
* String key => Object value.
* @return converted map
*/
public Map<String, Object> asTestSideMap() {
return asMap(SERVER_SIDE);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Headers headers = (Headers) o;
return Objects.equals(entries, headers.entries);
}
@Override
public int hashCode() {
return Objects.hash(entries);
}
@Override
public String toString() {
return "Headers{" + "\nentries=" + entries + '}';
}
public MediaTypes getMediaTypes() {
return mediaTypes;
}
public void setMediaTypes(MediaTypes mediaTypes) {
this.mediaTypes = mediaTypes;
}
public HttpHeaders getHttpHeaders() {
return httpHeaders;
}
public void setHttpHeaders(HttpHeaders httpHeaders) {
this.httpHeaders = httpHeaders;
}
public MessagingHeaders getMessagingHeaders() {
return messagingHeaders;
}
public void setMessagingHeaders(MessagingHeaders messagingHeaders) {
this.messagingHeaders = messagingHeaders;
}
public Set<Header> getEntries() {
return entries;
}
public void setEntries(Set<Header> entries) {
this.entries = entries;
}
public String messagingContentType() {
return messagingHeaders.messagingContentType();
}
public String accept() {
return httpHeaders.accept();
}
public String acceptCharset() {
return httpHeaders.acceptCharset();
}
public String acceptEncoding() {
return httpHeaders.acceptEncoding();
}
public String acceptLanguage() {
return httpHeaders.acceptLanguage();
}
public String acceptRanges() {
return httpHeaders.acceptRanges();
}
public String accessControlAllowCredentials() {
return httpHeaders.accessControlAllowCredentials();
}
public String accessControlAllowHeaders() {
return httpHeaders.accessControlAllowHeaders();
}
public String accessControlAllowMethods() {
return httpHeaders.accessControlAllowMethods();
}
public String accessControlAllowOrigin() {
return httpHeaders.accessControlAllowOrigin();
}
public String accessControlExposeHeaders() {
return httpHeaders.accessControlExposeHeaders();
}
public String accessControlMaxAge() {
return httpHeaders.accessControlMaxAge();
}
public String accessControlRequestHeaders() {
return httpHeaders.accessControlRequestHeaders();
}
public String accessControlRequestMethod() {
return httpHeaders.accessControlRequestMethod();
}
public String age() {
return httpHeaders.age();
}
public String allow() {
return httpHeaders.allow();
}
public String authorization() {
return httpHeaders.authorization();
}
public String cacheControl() {
return httpHeaders.cacheControl();
}
public String connection() {
return httpHeaders.connection();
}
public String contentEncoding() {
return httpHeaders.contentEncoding();
}
public String contentDisposition() {
return httpHeaders.contentDisposition();
}
public String contentLanguage() {
return httpHeaders.contentLanguage();
}
public String contentLength() {
return httpHeaders.contentLength();
}
public String contentLocation() {
return httpHeaders.contentLocation();
}
public String contentRange() {
return httpHeaders.contentRange();
}
public String contentType() {
return httpHeaders.contentType();
}
public String cookie() {
return httpHeaders.cookie();
}
public String date() {
return httpHeaders.date();
}
public String etag() {
return httpHeaders.etag();
}
public String expect() {
return httpHeaders.expect();
}
public String expires() {
return httpHeaders.expires();
}
public String from() {
return httpHeaders.from();
}
public String host() {
return httpHeaders.host();
}
public String ifMatch() {
return httpHeaders.ifMatch();
}
public String ifModifiedSince() {
return httpHeaders.ifModifiedSince();
}
public String ifNoneMatch() {
return httpHeaders.ifNoneMatch();
}
public String ifRange() {
return httpHeaders.ifRange();
}
public String ifUnmodifiedSince() {
return httpHeaders.ifUnmodifiedSince();
}
public String lastModified() {
return httpHeaders.lastModified();
}
public String link() {
return httpHeaders.link();
}
public String location() {
return httpHeaders.location();
}
public String max_forwards() {
return httpHeaders.max_forwards();
}
public String origin() {
return httpHeaders.origin();
}
public String pragma() {
return httpHeaders.pragma();
}
public String proxyAuthenticate() {
return httpHeaders.proxyAuthenticate();
}
public String proxyAuthorization() {
return httpHeaders.proxyAuthorization();
}
public String range() {
return httpHeaders.range();
}
public String referer() {
return httpHeaders.referer();
}
public String retryAfter() {
return httpHeaders.retryAfter();
}
public String server() {
return httpHeaders.server();
}
public String setCookie() {
return httpHeaders.setCookie();
}
public String setCookie2() {
return httpHeaders.setCookie2();
}
public String te() {
return httpHeaders.te();
}
public String trailer() {
return httpHeaders.trailer();
}
public String transferEncoding() {
return httpHeaders.transferEncoding();
}
public String upgrade() {
return httpHeaders.upgrade();
}
public String user_agent() {
return httpHeaders.user_agent();
}
public String vary() {
return httpHeaders.vary();
}
public String via() {
return httpHeaders.via();
}
public String warning() {
return httpHeaders.warning();
}
public String wwwAuthenticate() {
return httpHeaders.wwwAuthenticate();
}
public String allValue() {
return mediaTypes.allValue();
}
public String applicationAtomXml() {
return mediaTypes.applicationAtomXml();
}
public String applicationFormUrlencoded() {
return mediaTypes.applicationFormUrlencoded();
}
public String applicationJson() {
return mediaTypes.applicationJson();
}
public String applicationJsonUtf8() {
return mediaTypes.applicationJsonUtf8();
}
public String applicationOctetStream() {
return mediaTypes.applicationOctetStream();
}
public String applicationPdf() {
return mediaTypes.applicationPdf();
}
public String applicationXhtmlXml() {
return mediaTypes.applicationXhtmlXml();
}
public String applicationXml() {
return mediaTypes.applicationXml();
}
public String imageGif() {
return mediaTypes.imageGif();
}
public String imageJpeg() {
return mediaTypes.imageJpeg();
}
public String imagePng() {
return mediaTypes.imagePng();
}
public String multipartFormData() {
return mediaTypes.multipartFormData();
}
public String textHtml() {
return mediaTypes.textHtml();
}
public String textMarkdown() {
return mediaTypes.textMarkdown();
}
public String textPlain() {
return mediaTypes.textPlain();
}
public String textXml() {
return mediaTypes.textXml();
}
}

View File

@@ -0,0 +1,555 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Contains most commonly used http headers.
*
* @author Marcin Grzejszczak
* @since 1.0.2
*/
public class HttpHeaders {
/**
* The HTTP {@code Accept} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-5.3.2" > Section 5.3.2 of
* RFC 7231</a>
*/
public String accept() {
return "Accept";
}
/**
* The HTTP {@code Accept-Charset} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-5.3.3" > Section 5.3.3 of
* RFC 7231</a>
*/
public String acceptCharset() {
return "Accept-Charset";
}
/**
* The HTTP {@code Accept-Encoding} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-5.3.4" > Section 5.3.4 of
* RFC 7231</a>
*/
public String acceptEncoding() {
return "Accept-Encoding";
}
/**
* The HTTP {@code Accept-Language} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-5.3.5" > Section 5.3.5 of
* RFC 7231</a>
*/
public String acceptLanguage() {
return "Accept-Language";
}
/**
* The HTTP {@code Accept-Ranges} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7233#section-2.3" > Section 5.3.5 of
* RFC 7233</a>
*/
public String acceptRanges() {
return "Accept-Ranges";
}
/**
* The CORS {@code Access-Control-Allow-Credentials} response header field name.
* @see <ahref="https://www.w3.org/TR/cors/" > CORS W3C recommendation</a>
*/
public String accessControlAllowCredentials() {
return "Access-Control-Allow-Credentials";
}
/**
* The CORS {@code Access-Control-Allow-Headers} response header field name.
* @see <ahref="https://www.w3.org/TR/cors/" > CORS W3C recommendation</a>
*/
public String accessControlAllowHeaders() {
return "Access-Control-Allow-Headers";
}
/**
* The CORS {@code Access-Control-Allow-Methods} response header field name.
* @see <ahref="https://www.w3.org/TR/cors/" > CORS W3C recommendation</a>
*/
public String accessControlAllowMethods() {
return "Access-Control-Allow-Methods";
}
/**
* The CORS {@code Access-Control-Allow-Origin} response header field name.
* @see <ahref="https://www.w3.org/TR/cors/" > CORS W3C recommendation</a>
*/
public String accessControlAllowOrigin() {
return "Access-Control-Allow-Origin";
}
/**
* The CORS {@code Access-Control-Expose-Headers} response header field name.
* @see <ahref="https://www.w3.org/TR/cors/" > CORS W3C recommendation</a>
*/
public String accessControlExposeHeaders() {
return "Access-Control-Expose-Headers";
}
/**
* The CORS {@code Access-Control-Max-Age} response header field name.
* @see <ahref="https://www.w3.org/TR/cors/" > CORS W3C recommendation</a>
*/
public String accessControlMaxAge() {
return "Access-Control-Max-Age";
}
/**
* The CORS {@code Access-Control-Request-Headers} request header field name.
* @see <ahref="https://www.w3.org/TR/cors/" > CORS W3C recommendation</a>
*/
public String accessControlRequestHeaders() {
return "Access-Control-Request-Headers";
}
/**
* The CORS {@code Access-Control-Request-Method} request header field name.
* @see <ahref="https://www.w3.org/TR/cors/" > CORS W3C recommendation</a>
*/
public String accessControlRequestMethod() {
return "Access-Control-Request-Method";
}
/**
* The HTTP {@code Age} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7234#section-5.1" > Section 5.1 of RFC
* 7234</a>
*/
public String age() {
return "Age";
}
/**
* The HTTP {@code Allow} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-7.4.1" > Section 7.4.1 of
* RFC 7231</a>
*/
public String allow() {
return "Allow";
}
/**
* The HTTP {@code Authorization} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7235#section-4.2" > Section 4.2 of RFC
* 7235</a>
*/
public String authorization() {
return "Authorization";
}
/**
* The HTTP {@code Cache-Control} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7234#section-5.2" > Section 5.2 of RFC
* 7234</a>
*/
public String cacheControl() {
return "Cache-Control";
}
/**
* The HTTP {@code Connection} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7230#section-6.1" > Section 6.1 of RFC
* 7230</a>
*/
public String connection() {
return "Connection";
}
/**
* The HTTP {@code Content-Encoding} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-3.1.2.2" > Section 3.1.2.2
* of RFC 7231</a>
*/
public String contentEncoding() {
return "Content-Encoding";
}
/**
* The HTTP {@code Content-Disposition} header field name
* @see <ahref="https://tools.ietf.org/html/rfc6266" > RFC 6266</a>
*/
public String contentDisposition() {
return "Content-Disposition";
}
/**
* The HTTP {@code Content-Language} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-3.1.3.2" > Section 3.1.3.2
* of RFC 7231</a>
*/
public String contentLanguage() {
return "Content-Language";
}
/**
* The HTTP {@code Content-Length} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7230#section-3.3.2" > Section 3.3.2 of
* RFC 7230</a>
*/
public String contentLength() {
return "Content-Length";
}
/**
* The HTTP {@code Content-Location} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-3.1.4.2" > Section 3.1.4.2
* of RFC 7231</a>
*/
public String contentLocation() {
return "Content-Location";
}
/**
* The HTTP {@code Content-Range} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7233#section-4.2" > Section 4.2 of RFC
* 7233</a>
*/
public String contentRange() {
return "Content-Range";
}
/**
* The HTTP {@code Content-Type} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-3.1.1.5" > Section 3.1.1.5
* of RFC 7231</a>
*/
public String contentType() {
return "Content-Type";
}
/**
* The HTTP {@code Cookie} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc2109#section-4.3.4" > Section 4.3.4 of
* RFC 2109</a>
*/
public String cookie() {
return "Cookie";
}
/**
* The HTTP {@code Date} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-7.1.1.2" > Section 7.1.1.2
* of RFC 7231</a>
*/
public String date() {
return "Date";
}
/**
* The HTTP {@code ETag} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7232#section-2.3" > Section 2.3 of RFC
* 7232</a>
*/
public String etag() {
return "ETag";
}
/**
* The HTTP {@code Expect} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-5.1.1" > Section 5.1.1 of
* RFC 7231</a>
*/
public String expect() {
return "Expect";
}
/**
* The HTTP {@code Expires} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7234#section-5.3" > Section 5.3 of RFC
* 7234</a>
*/
public String expires() {
return "Expires";
}
/**
* The HTTP {@code From} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-5.5.1" > Section 5.5.1 of
* RFC 7231</a>
*/
public String from() {
return "From";
}
/**
* The HTTP {@code Host} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7230#section-5.4" > Section 5.4 of RFC
* 7230</a>
*/
public String host() {
return "Host";
}
/**
* The HTTP {@code If-Match} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7232#section-3.1" > Section 3.1 of RFC
* 7232</a>
*/
public String ifMatch() {
return "If-Match";
}
/**
* The HTTP {@code If-Modified-Since} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7232#section-3.3" > Section 3.3 of RFC
* 7232</a>
*/
public String ifModifiedSince() {
return "If-Modified-Since";
}
/**
* The HTTP {@code If-None-Match} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7232#section-3.2" > Section 3.2 of RFC
* 7232</a>
*/
public String ifNoneMatch() {
return "If-None-Match";
}
/**
* The HTTP {@code If-Range} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7233#section-3.2" > Section 3.2 of RFC
* 7233</a>
*/
public String ifRange() {
return "If-Range";
}
/**
* The HTTP {@code If-Unmodified-Since} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7232#section-3.4" > Section 3.4 of RFC
* 7232</a>
*/
public String ifUnmodifiedSince() {
return "If-Unmodified-Since";
}
/**
* The HTTP {@code Last-Modified} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7232#section-2.2" > Section 2.2 of RFC
* 7232</a>
*/
public String lastModified() {
return "Last-Modified";
}
/**
* The HTTP {@code Link} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc5988" > RFC 5988</a>
*/
public String link() {
return "Link";
}
/**
* The HTTP {@code Location} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-7.1.2" > Section 7.1.2 of
* RFC 7231</a>
*/
public String location() {
return "Location";
}
/**
* The HTTP {@code Max-Forwards} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-5.1.2" > Section 5.1.2 of
* RFC 7231</a>
*/
public String max_forwards() {
return "Max-Forwards";
}
/**
* The HTTP {@code Origin} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc6454" > RFC 6454</a>
*/
public String origin() {
return "Origin";
}
/**
* The HTTP {@code Pragma} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7234#section-5.4" > Section 5.4 of RFC
* 7234</a>
*/
public String pragma() {
return "Pragma";
}
/**
* The HTTP {@code Proxy-Authenticate} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7235#section-4.3" > Section 4.3 of RFC
* 7235</a>
*/
public String proxyAuthenticate() {
return "Proxy-Authenticate";
}
/**
* The HTTP {@code Proxy-Authorization} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7235#section-4.4" > Section 4.4 of RFC
* 7235</a>
*/
public String proxyAuthorization() {
return "Proxy-Authorization";
}
/**
* The HTTP {@code Range} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7233#section-3.1" > Section 3.1 of RFC
* 7233</a>
*/
public String range() {
return "Range";
}
/**
* The HTTP {@code Referer} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-5.5.2" > Section 5.5.2 of
* RFC 7231</a>
*/
public String referer() {
return "Referer";
}
/**
* The HTTP {@code Retry-After} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-7.1.3" > Section 7.1.3 of
* RFC 7231</a>
*/
public String retryAfter() {
return "Retry-After";
}
/**
* The HTTP {@code Server} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-7.4.2" > Section 7.4.2 of
* RFC 7231</a>
*/
public String server() {
return "Server";
}
/**
* The HTTP {@code Set-Cookie} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc2109#section-4.2.2" > Section 4.2.2 of
* RFC 2109</a>
*/
public String setCookie() {
return "Set-Cookie";
}
/**
* The HTTP {@code Set-Cookie2} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc2965" > RFC 2965</a>
*/
public String setCookie2() {
return "Set-Cookie2";
}
/**
* The HTTP {@code TE} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7230#section-4.3" > Section 4.3 of RFC
* 7230</a>
*/
public String te() {
return "TE";
}
/**
* The HTTP {@code Trailer} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7230#section-4.4" > Section 4.4 of RFC
* 7230</a>
*/
public String trailer() {
return "Trailer";
}
/**
* The HTTP {@code Transfer-Encoding} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7230#section-3.3.1" > Section 3.3.1 of
* RFC 7230</a>
*/
public String transferEncoding() {
return "Transfer-Encoding";
}
/**
* The HTTP {@code Upgrade} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7230#section-6.7" > Section 6.7 of RFC
* 7230</a>
*/
public String upgrade() {
return "Upgrade";
}
/**
* The HTTP {@code User-Agent} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-5.5.3" > Section 5.5.3 of
* RFC 7231</a>
*/
public String user_agent() {
return "User-Agent";
}
/**
* The HTTP {@code Vary} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-7.1.4" > Section 7.1.4 of
* RFC 7231</a>
*/
public String vary() {
return "Vary";
}
/**
* The HTTP {@code Via} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7230#section-5.7.1" > Section 5.7.1 of
* RFC 7230</a>
*/
public String via() {
return "Via";
}
/**
* The HTTP {@code Warning} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7234#section-5.5" > Section 5.5 of RFC
* 7234</a>
*/
public String warning() {
return "Warning";
}
/**
* The HTTP {@code WWW-Authenticate} header field name.
* @see <ahref="https://tools.ietf.org/html/rfc7235#section-4.1" > Section 4.1 of RFC
* 7235</a>
*/
public String wwwAuthenticate() {
return "WWW-Authenticate";
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Contains Http Methods.
*
* @author Marcin Grzejszczak
* @since 1.0.2
*/
public class HttpMethods {
public HttpMethod GET() {
return HttpMethod.GET;
}
public HttpMethod HEAD() {
return HttpMethod.HEAD;
}
public HttpMethod POST() {
return HttpMethod.POST;
}
public HttpMethod PUT() {
return HttpMethod.PUT;
}
public HttpMethod PATCH() {
return HttpMethod.PATCH;
}
public HttpMethod DELETE() {
return HttpMethod.DELETE;
}
public HttpMethod OPTIONS() {
return HttpMethod.OPTIONS;
}
public HttpMethod TRACE() {
return HttpMethod.TRACE;
}
public enum HttpMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
}
}

View File

@@ -0,0 +1,635 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Helper functions for HTTP statuses.
*
* @author Marcin Grzejszczak
* @since 2.0.0
*/
public class HttpStatus {
/**
* {@code 100 Continue}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.2.1" > HTTP/1.1:
* Semantics and Content, section 6.2.1</a>
*/
public int CONTINUE() {
return 100;
}
/**
* {@code 101 Switching Protocols}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.2.2" > HTTP/1.1:
* Semantics and Content, section 6.2.2</a>
*/
public int SWITCHING_PROTOCOLS() {
return 101;
}
/**
* {@code 102 Processing}.
* @see <ahref="https://tools.ietf.org/html/rfc2518#section-10.1" > WebDAV</a>
*/
public int PROCESSING() {
return 102;
}
/**
* {@code 103 Checkpoint}.
* @see <ahref="https://code.google.com/p/gears/wiki/ResumableHttpRequestsProposal" >
* A proposal for supporting resumable POST/PUT HTTP requests in HTTP/1.0</a>
*/
public int CHECKPOINT() {
return 103;
}
/**
* {@code 200 OK}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.3.1" > HTTP/1.1:
* Semantics and Content, section 6.3.1</a>
*/
public int OK() {
return 200;
}
/**
* {@code 201 Created}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.3.2" > HTTP/1.1:
* Semantics and Content, section 6.3.2</a>
*/
public int CREATED() {
return 201;
}
/**
* {@code 202 Accepted}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.3.3" > HTTP/1.1:
* Semantics and Content, section 6.3.3</a>
*/
public int ACCEPTED() {
return 202;
}
/**
* {@code 203 Non-Authoritative Information}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.3.4" > HTTP/1.1:
* Semantics and Content, section 6.3.4</a>
*/
public int NON_AUTHORITATIVE_INFORMATION() {
return 203;
}
/**
* {@code 204 No Content}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.3.5" > HTTP/1.1:
* Semantics and Content, section 6.3.5</a>
*/
public int NO_CONTENT() {
return 204;
}
/**
* {@code 205 Reset Content}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.3.6" > HTTP/1.1:
* Semantics and Content, section 6.3.6</a>
*/
public int RESET_CONTENT() {
return 205;
}
/**
* {@code 206 Partial Content}.
* @see <ahref="https://tools.ietf.org/html/rfc7233#section-4.1" > HTTP/1.1: Range
* Requests, section 4.1</a>
*/
public int PARTIAL_CONTENT() {
return 206;
}
/**
* {@code 207 Multi-Status}.
* @see <ahref="https://tools.ietf.org/html/rfc4918#section-13" > WebDAV</a>
*/
public int MULTI_STATUS() {
return 207;
}
/**
* {@code 208 Already Reported}.
* @see <ahref="https://tools.ietf.org/html/rfc5842#section-7.1" > WebDAV Binding
* Extensions</a>
*/
public int ALREADY_REPORTED() {
return 208;
}
/**
* {@code 226 IM Used}.
* @see <ahref="https://tools.ietf.org/html/rfc3229#section-10.4.1" > Delta encoding
* in HTTP</a>
*/
public int IM_USED() {
return 226;
}
/**
* {@code 300 Multiple Choices}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.4.1" > HTTP/1.1:
* Semantics and Content, section 6.4.1</a>
*/
public int MULTIPLE_CHOICES() {
return 300;
}
/**
* {@code 301 Moved Permanently}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.4.2" > HTTP/1.1:
* Semantics and Content, section 6.4.2</a>
*/
public int MOVED_PERMANENTLY() {
return 301;
}
/**
* {@code 302 Found}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.4.3" > HTTP/1.1:
* Semantics and Content, section 6.4.3</a>
*/
public int FOUND() {
return 302;
}
/**
* {@code 302 Moved Temporarily}.
* @see <ahref="https://tools.ietf.org/html/rfc1945#section-9.3" > HTTP/1.0, section
* 9.3</a>
* @deprecated in favor of {@link #FOUND} which will be returned from
* {@code HttpStatus.valueOf(302)}
*/
@Deprecated
public int MOVED_TEMPORARILY() {
return 302;
}
/**
* {@code 303 See Other}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.4.4" > HTTP/1.1:
* Semantics and Content, section 6.4.4</a>
*/
public int SEE_OTHER() {
return 303;
}
/**
* {@code 304 Not Modified}.
* @see <ahref="https://tools.ietf.org/html/rfc7232#section-4.1" > HTTP/1.1:
* Conditional Requests, section 4.1</a>
*/
public int NOT_MODIFIED() {
return 304;
}
/**
* {@code 305 Use Proxy}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.4.5" > HTTP/1.1:
* Semantics and Content, section 6.4.5</a>
* @deprecated due to security concerns regarding in-band configuration of a proxy
*/
@Deprecated
public int USE_PROXY() {
return 305;
}
/**
* {@code 307 Temporary Redirect}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.4.7" > HTTP/1.1:
* Semantics and Content, section 6.4.7</a>
*/
public int TEMPORARY_REDIRECT() {
return 307;
}
/**
* {@code 308 Permanent Redirect}.
* @see <ahref="https://tools.ietf.org/html/rfc7238" > RFC 7238</a>
*/
public int PERMANENT_REDIRECT() {
return 308;
}
/**
* {@code 400 Bad Request}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.1" > HTTP/1.1:
* Semantics and Content, section 6.5.1</a>
*/
public int BAD_REQUEST() {
return 400;
}
/**
* {@code 401 Unauthorized}.
* @see <ahref="https://tools.ietf.org/html/rfc7235#section-3.1" > HTTP/1.1:
* Authentication, section 3.1</a>
*/
public int UNAUTHORIZED() {
return 401;
}
/**
* {@code 402 Payment Required}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.2" > HTTP/1.1:
* Semantics and Content, section 6.5.2</a>
*/
public int PAYMENT_REQUIRED() {
return 402;
}
/**
* {@code 403 Forbidden}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.3" > HTTP/1.1:
* Semantics and Content, section 6.5.3</a>
*/
public int FORBIDDEN() {
return 403;
}
/**
* {@code 404 Not Found}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.4" > HTTP/1.1:
* Semantics and Content, section 6.5.4</a>
*/
public int NOT_FOUND() {
return 404;
}
/**
* {@code 405 Method Not Allowed}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.5" > HTTP/1.1:
* Semantics and Content, section 6.5.5</a>
*/
public int METHOD_NOT_ALLOWED() {
return 405;
}
/**
* {@code 406 Not Acceptable}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.6" > HTTP/1.1:
* Semantics and Content, section 6.5.6</a>
*/
public int NOT_ACCEPTABLE() {
return 406;
}
/**
* {@code 407 Proxy Authentication Required}.
* @see <ahref="https://tools.ietf.org/html/rfc7235#section-3.2" > HTTP/1.1:
* Authentication, section 3.2</a>
*/
public int PROXY_AUTHENTICATION_REQUIRED() {
return 407;
}
/**
* {@code 408 Request Timeout}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.7" > HTTP/1.1:
* Semantics and Content, section 6.5.7</a>
*/
public int REQUEST_TIMEOUT() {
return 408;
}
/**
* {@code 409 Conflict}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.8" > HTTP/1.1:
* Semantics and Content, section 6.5.8</a>
*/
public int CONFLICT() {
return 409;
}
/**
* {@code 410 Gone}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.9" > HTTP/1.1:
* Semantics and Content, section 6.5.9</a>
*/
public int GONE() {
return 410;
}
/**
* {@code 411 Length Required}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.10" > HTTP/1.1:
* Semantics and Content, section 6.5.10</a>
*/
public int LENGTH_REQUIRED() {
return 411;
}
/**
* {@code 412 Precondition failed}.
* @see <ahref="https://tools.ietf.org/html/rfc7232#section-4.2" > HTTP/1.1:
* Conditional Requests, section 4.2</a>
*/
public int PRECONDITION_FAILED() {
return 412;
}
/**
* {@code 413 Payload Too Large}.
* @since 4.1* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.11" >
* HTTP/1.1: Semantics and Content, section 6.5.11</a>
*/
public int PAYLOAD_TOO_LARGE() {
return 413;
}
/**
* {@code 413 Request Entity Too Large}.
* @see <ahref="https://tools.ietf.org/html/rfc2616#section-10.4.14" > HTTP/1.1,
* section 10.4.14</a>
* @deprecated in favor of {@link #PAYLOAD_TOO_LARGE} which will be returned from
* {@code HttpStatus.valueOf(413)}
*/
@Deprecated
public int REQUEST_ENTITY_TOO_LARGE() {
return 413;
}
/**
* {@code 414 URI Too Long}.
* @since 4.1* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.12" >
* HTTP/1.1: Semantics and Content, section 6.5.12</a>
*/
public int URI_TOO_LONG() {
return 414;
}
/**
* {@code 414 Request-URI Too Long}.
* @see <ahref="https://tools.ietf.org/html/rfc2616#section-10.4.15" > HTTP/1.1,
* section 10.4.15</a>
* @deprecated in favor of {@link #URI_TOO_LONG} which will be returned from
* {@code HttpStatus.valueOf(414)}
*/
@Deprecated
public int REQUEST_URI_TOO_LONG() {
return 414;
}
/**
* {@code 415 Unsupported Media Type}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.13" > HTTP/1.1:
* Semantics and Content, section 6.5.13</a>
*/
public int UNSUPPORTED_MEDIA_TYPE() {
return 415;
}
/**
* {@code 416 Requested Range Not Satisfiable}.
* @see <ahref="https://tools.ietf.org/html/rfc7233#section-4.4" > HTTP/1.1: Range
* Requests, section 4.4</a>
*/
public int REQUESTED_RANGE_NOT_SATISFIABLE() {
return 416;
}
/**
* {@code 417 Expectation Failed}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.5.14" > HTTP/1.1:
* Semantics and Content, section 6.5.14</a>
*/
public int EXPECTATION_FAILED() {
return 417;
}
/**
* {@code 418 I'm a teapot}.
* @see <ahref="https://tools.ietf.org/html/rfc2324#section-2.3.2" > HTCPCP/1.0</a>
*/
public int I_AM_A_TEAPOT() {
return 418;
}
/**
* @deprecated See <a href=
* "https://tools.ietf.org/rfcdiff?difftype=--hwdiff&url2=draft-ietf-webdav-protocol-06.txt">WebDAV
* Draft Changes</a>
*/
@Deprecated
public int INSUFFICIENT_SPACE_ON_RESOURCE() {
return 419;
}
/**
* @deprecated See <a href=
* "https://tools.ietf.org/rfcdiff?difftype=--hwdiff&url2=draft-ietf-webdav-protocol-06.txt">WebDAV
* Draft Changes</a>
*/
@Deprecated
public int METHOD_FAILURE() {
return 420;
}
/**
* @deprecated See <a href=
* "https://tools.ietf.org/rfcdiff?difftype=--hwdiff&url2=draft-ietf-webdav-protocol-06.txt">WebDAV
* Draft Changes</a>
*/
@Deprecated
public int DESTINATION_LOCKED() {
return 421;
}
/**
* {@code 422 Unprocessable Entity}.
* @see <ahref="https://tools.ietf.org/html/rfc4918#section-11.2" > WebDAV</a>
*/
public int UNPROCESSABLE_ENTITY() {
return 422;
}
/**
* {@code 423 Locked}.
* @see <ahref="https://tools.ietf.org/html/rfc4918#section-11.3" > WebDAV</a>
*/
public int LOCKED() {
return 423;
}
/**
* {@code 424 Failed Dependency}.
* @see <ahref="https://tools.ietf.org/html/rfc4918#section-11.4" > WebDAV</a>
*/
public int FAILED_DEPENDENCY() {
return 424;
}
/**
* {@code 426 Upgrade Required}.
* @see <ahref="https://tools.ietf.org/html/rfc2817#section-6" > Upgrading to TLS
* Within HTTP/1.1</a>
*/
public int UPGRADE_REQUIRED() {
return 426;
}
/**
* {@code 428 Precondition Required}.
* @see <ahref="https://tools.ietf.org/html/rfc6585#section-3" > Additional HTTP
* Status Codes</a>
*/
public int PRECONDITION_REQUIRED() {
return 428;
}
/**
* {@code 429 Too Many Requests}.
* @see <ahref="https://tools.ietf.org/html/rfc6585#section-4" > Additional HTTP
* Status Codes</a>
*/
public int TOO_MANY_REQUESTS() {
return 429;
}
/**
* {@code 431 Request Header Fields Too Large}.
* @see <ahref="https://tools.ietf.org/html/rfc6585#section-5" > Additional HTTP
* Status Codes</a>
*/
public int REQUEST_HEADER_FIELDS_TOO_LARGE() {
return 431;
}
/**
* {@code 451 Unavailable For Legal Reasons}.
* @see <ahref="https://tools.ietf.org/html/draft-ietf-httpbis-legally-restricted-status-04"
* > An HTTP Status Code to Report Legal Obstacles</a>
* @since 4.3
*/
public int UNAVAILABLE_FOR_LEGAL_REASONS() {
return 451;
}
/**
* {@code 500 Internal Server Error}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.6.1" > HTTP/1.1:
* Semantics and Content, section 6.6.1</a>
*/
public int INTERNAL_SERVER_ERROR() {
return 500;
}
/**
* {@code 501 Not Implemented}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.6.2" > HTTP/1.1:
* Semantics and Content, section 6.6.2</a>
*/
public int NOT_IMPLEMENTED() {
return 501;
}
/**
* {@code 502 Bad Gateway}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.6.3" > HTTP/1.1:
* Semantics and Content, section 6.6.3</a>
*/
public int BAD_GATEWAY() {
return 502;
}
/**
* {@code 503 Service Unavailable}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.6.4" > HTTP/1.1:
* Semantics and Content, section 6.6.4</a>
*/
public int SERVICE_UNAVAILABLE() {
return 503;
}
/**
* {@code 504 Gateway Timeout}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.6.5" > HTTP/1.1:
* Semantics and Content, section 6.6.5</a>
*/
public int GATEWAY_TIMEOUT() {
return 504;
}
/**
* {@code 505 HTTP Version Not Supported}.
* @see <ahref="https://tools.ietf.org/html/rfc7231#section-6.6.6" > HTTP/1.1:
* Semantics and Content, section 6.6.6</a>
*/
public int HTTP_VERSION_NOT_SUPPORTED() {
return 505;
}
/**
* {@code 506 Variant Also Negotiates}
* @see <ahref="https://tools.ietf.org/html/rfc2295#section-8.1" > Transparent Content
* Negotiation</a>
*/
public int VARIANT_ALSO_NEGOTIATES() {
return 506;
}
/**
* {@code 507 Insufficient Storage}
* @see <ahref="https://tools.ietf.org/html/rfc4918#section-11.5" > WebDAV</a>
*/
public int INSUFFICIENT_STORAGE() {
return 507;
}
/**
* {@code 508 Loop Detected}
* @see <ahref="https://tools.ietf.org/html/rfc5842#section-7.2" > WebDAV Binding
* Extensions</a>
*/
public int LOOP_DETECTED() {
return 508;
}
/**
* {@code 509 Bandwidth Limit Exceeded}
*/
public int BANDWIDTH_LIMIT_EXCEEDED() {
return 509;
}
/**
* {@code 510 Not Extended}
* @see <ahref="https://tools.ietf.org/html/rfc2774#section-7" > HTTP Extension
* Framework</a>
*/
public int NOT_EXTENDED() {
return 510;
}
/**
* {@code 511 Network Authentication Required}.
* @see <ahref="https://tools.ietf.org/html/rfc6585#section-6" > Additional HTTP
* Status Codes</a>
*/
public int NETWORK_AUTHENTICATION_REQUIRED() {
return 511;
}
}

View File

@@ -0,0 +1,405 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Represents an input for messaging. The input can be a message or some action inside the
* application.
*
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @since 1.0.0
*/
public class Input extends Common implements RegexCreatingProperty<ClientDslProperty> {
private static final Log log = LogFactory.getLog(Input.class);
private ClientPatternValueDslProperty property = new ClientPatternValueDslProperty();
private DslProperty<String> messageFrom;
private ExecutionProperty triggeredBy;
private Headers messageHeaders = new Headers();
private BodyType messageBody;
private ExecutionProperty assertThat;
private BodyMatchers bodyMatchers;
public Input() {
}
public Input(Input input) {
this.messageFrom = input.getMessageFrom();
this.messageHeaders = input.getMessageHeaders();
this.messageBody = input.getMessageBody();
}
/**
* Name of a destination from which message would come to trigger action in the
* system.
* @param messageFrom message destination
*/
public void messageFrom(String messageFrom) {
this.messageFrom = new DslProperty<>(messageFrom);
}
/**
* Name of a destination from which message would come to trigger action in the
* system.
* @param messageFrom message destination
*/
public void messageFrom(DslProperty messageFrom) {
this.messageFrom = messageFrom;
}
/**
* Function that needs to be executed to trigger action in the system.
* @param triggeredBy method name that triggers the message
*/
public void triggeredBy(String triggeredBy) {
this.triggeredBy = new ExecutionProperty(triggeredBy);
}
public BodyType messageBody(Object bodyAsValue) {
this.messageBody = new BodyType(bodyAsValue);
return this.messageBody;
}
public DslProperty value(ClientDslProperty client) {
Object dynamicValue = client.getClientValue();
Object concreteValue = client.getServerValue();
if (dynamicValue instanceof RegexProperty) {
return ((RegexProperty) dynamicValue).dynamicClientConcreteProducer();
}
return new DslProperty(dynamicValue, concreteValue);
}
public DslProperty value(RegexProperty prop) {
return value(client(prop));
}
public DslProperty $(RegexProperty prop) {
return value(client(prop));
}
public DslProperty $(ClientDslProperty client) {
return value(client);
}
@Override
public RegexProperty regexProperty(Object object) {
return new RegexProperty(object).dynamicClientConcreteProducer();
}
public void assertThat(String assertThat) {
this.assertThat = new ExecutionProperty(assertThat);
}
public ClientPatternValueDslProperty getProperty() {
return property;
}
public void setProperty(ClientPatternValueDslProperty property) {
this.property = property;
}
public DslProperty<String> getMessageFrom() {
return messageFrom;
}
public void setMessageFrom(DslProperty<String> messageFrom) {
this.messageFrom = messageFrom;
}
public ExecutionProperty getTriggeredBy() {
return triggeredBy;
}
public void setTriggeredBy(ExecutionProperty triggeredBy) {
this.triggeredBy = triggeredBy;
}
public Headers getMessageHeaders() {
return messageHeaders;
}
public void setMessageHeaders(Headers messageHeaders) {
this.messageHeaders = messageHeaders;
}
public BodyType getMessageBody() {
return messageBody;
}
public void setMessageBody(BodyType messageBody) {
this.messageBody = messageBody;
}
public ExecutionProperty getAssertThat() {
return assertThat;
}
public void setAssertThat(ExecutionProperty assertThat) {
this.assertThat = assertThat;
}
public BodyMatchers getBodyMatchers() {
return bodyMatchers;
}
public void setBodyMatchers(BodyMatchers bodyMatchers) {
this.bodyMatchers = bodyMatchers;
}
@Override
public ClientDslProperty anyAlphaUnicode() {
return property.anyAlphaUnicode();
}
@Override
public ClientDslProperty anyAlphaNumeric() {
return property.anyAlphaNumeric();
}
@Override
public ClientDslProperty anyNumber() {
return property.anyNumber();
}
@Override
public ClientDslProperty anyInteger() {
return property.anyInteger();
}
@Override
public ClientDslProperty anyPositiveInt() {
return property.anyPositiveInt();
}
@Override
public ClientDslProperty anyDouble() {
return property.anyDouble();
}
@Override
public ClientDslProperty anyHex() {
return property.anyHex();
}
@Override
public ClientDslProperty aBoolean() {
return property.aBoolean();
}
@Override
public ClientDslProperty anyIpAddress() {
return property.anyIpAddress();
}
@Override
public ClientDslProperty anyHostname() {
return property.anyHostname();
}
@Override
public ClientDslProperty anyEmail() {
return property.anyEmail();
}
@Override
public ClientDslProperty anyUrl() {
return property.anyUrl();
}
@Override
public ClientDslProperty anyHttpsUrl() {
return property.anyHttpsUrl();
}
@Override
public ClientDslProperty anyUuid() {
return property.anyUuid();
}
@Override
public ClientDslProperty anyDate() {
return property.anyDate();
}
@Override
public ClientDslProperty anyDateTime() {
return property.anyDateTime();
}
@Override
public ClientDslProperty anyTime() {
return property.anyTime();
}
@Override
public ClientDslProperty anyIso8601WithOffset() {
return property.anyIso8601WithOffset();
}
@Override
public ClientDslProperty anyNonBlankString() {
return property.anyNonBlankString();
}
@Override
public ClientDslProperty anyNonEmptyString() {
return property.anyNonEmptyString();
}
@Override
public ClientDslProperty anyOf(String... values) {
return property.anyOf(values);
}
/**
* The message headers part of the contract.
* @param consumer function to manipulate the message headers
*/
public void messageHeaders(Consumer<Headers> consumer) {
this.messageHeaders = new Headers();
consumer.accept(this.messageHeaders);
}
/**
* The stub matchers part of the contract.
* @param consumer function to manipulate the body headers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
@Deprecated
public void stubMatchers(Consumer<BodyMatchers> consumer) {
log.warn("stubMatchers method is deprecated. Please use bodyMatchers instead");
bodyMatchers(consumer);
}
/**
* The stub matchers part of the contract.
* @param consumer function to manipulate the message headers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
public void bodyMatchers(Consumer<BodyMatchers> consumer) {
this.bodyMatchers = new BodyMatchers();
consumer.accept(this.bodyMatchers);
}
/**
* The message headers part of the contract.
* @param consumer function to manipulate the message headers
*/
public void messageHeaders(@DelegatesTo(Headers.class) Closure consumer) {
this.messageHeaders = new Headers();
consumer.setDelegate(this.messageHeaders);
consumer.call();
}
/**
* The stub matchers part of the contract.
* @param consumer function to manipulate the body headers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
@Deprecated
public void stubMatchers(@DelegatesTo(BodyMatchers.class) Closure consumer) {
log.warn("stubMatchers method is deprecated. Please use bodyMatchers instead");
bodyMatchers(consumer);
}
/**
* The stub matchers part of the contract.
* @param consumer function to manipulate the message headers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
public void bodyMatchers(@DelegatesTo(BodyMatchers.class) Closure consumer) {
this.bodyMatchers = new BodyMatchers();
consumer.setDelegate(this.bodyMatchers);
consumer.call();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Input input = (Input) o;
return Objects.equals(messageFrom, input.messageFrom)
&& Objects.equals(triggeredBy, input.triggeredBy)
&& Objects.equals(messageHeaders, input.messageHeaders)
&& Objects.equals(messageBody, input.messageBody)
&& Objects.equals(assertThat, input.assertThat)
&& Objects.equals(bodyMatchers, input.bodyMatchers);
}
@Override
public int hashCode() {
return Objects.hash(messageFrom, triggeredBy, messageHeaders, messageBody,
assertThat, bodyMatchers);
}
@Override
public String toString() {
return "Input{\n\tmessageFrom=" + messageFrom + ", \n\ttriggeredBy=" + triggeredBy
+ ", \n\tmessageHeaders=" + messageHeaders + ", \n\tmessageBody="
+ messageBody + ", \n\tassertThat=" + assertThat + ", \n\tbodyMatchers="
+ bodyMatchers + "} \n\t" + super.toString();
}
public static class BodyType extends DslProperty {
public BodyType(Object clientValue, Object serverValue) {
super(clientValue, serverValue);
}
public BodyType(Object singleValue) {
super(singleValue);
}
}
private class ClientPatternValueDslProperty
extends PatternValueDslProperty<ClientDslProperty> {
@Override
protected ClientDslProperty createProperty(Pattern pattern,
Object generatedValue) {
return new ClientDslProperty(pattern, generatedValue);
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Represents JSON comparison modes.
*
* @since 1.0.0
*/
public enum JSONCompareMode {
/**
* Strict checking. Not extensible, and strict array ordering.
*/
STRICT,
/**
* Lenient checking. Extensible, and non-strict array ordering.
*/
LENIENT,
/**
* Non-extensible checking. Not extensible, and non-strict array ordering.
*/
NON_EXTENSIBLE,
/**
* Strict order checking. Extensible, and strict array ordering.
*/
STRICT_ORDER;
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
/**
* Represents a matching strategy for a JSON.
*
* @since 1.0.0
*/
public class MatchingStrategy extends DslProperty {
private Type type;
private JSONCompareMode jsonCompareMode;
public MatchingStrategy(Object value, Type type) {
this(value, type, null);
}
public MatchingStrategy(Object value, Type type, JSONCompareMode jsonCompareMode) {
super(value);
this.type = type;
this.jsonCompareMode = jsonCompareMode;
}
public MatchingStrategy(DslProperty value, Type type) {
this(value, type, null);
}
public MatchingStrategy(DslProperty value, Type type,
JSONCompareMode jsonCompareMode) {
super(value.getClientValue(), value.getServerValue());
this.type = type;
this.jsonCompareMode = jsonCompareMode;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public JSONCompareMode getJsonCompareMode() {
return jsonCompareMode;
}
public void setJsonCompareMode(JSONCompareMode jsonCompareMode) {
this.jsonCompareMode = jsonCompareMode;
}
@Override
public String toString() {
return "MatchingStrategy{" + "type=" + type + ", jsonCompareMode="
+ jsonCompareMode + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
MatchingStrategy that = (MatchingStrategy) o;
return type == that.type && jsonCompareMode == that.jsonCompareMode;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), type, jsonCompareMode);
}
public enum Type {
/**
* Equality check.
*/
EQUAL_TO("equalTo"),
/**
* Contains check.
*/
CONTAINS("containing"),
/**
* Matching check.
*/
MATCHING("matching"),
/**
* Not matching check.
*/
NOT_MATCHING("notMatching"),
/**
* Equal to JSON check.
*/
EQUAL_TO_JSON("equalToJson"),
/**
* Equal to XML check.
*/
EQUAL_TO_XML("equalToXml"),
/**
* Absence check.
*/
ABSENT("absent"),
/**
* Binary equality check.
*/
BINARY_EQUAL_TO("binaryEqualTo");
private final String name;
Type(String name) {
this.name = name;
}
public final String getName() {
return name;
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Represents the type of matching the should be done against the body of the request or
* response.
*
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @author Olga Maciaszek-Sharma
* @since 1.0.3
*/
public enum MatchingType {
/**
* Verification by equality.
*/
EQUALITY,
/**
* Verification by type - is the type received in a body for the given path is of the
* same type. If it's a collection you can verify number of occurrences.
*/
TYPE,
/**
* Special version of regex for date check.
*/
DATE,
/**
* Special version of regex for time check.
*/
TIME,
/**
* Special version of regex for timestamp check.
*/
TIMESTAMP,
/**
* Verification if the value for the given path matches the provided regex.
*/
REGEX,
/**
* The user can provide custom command to execute.
*/
COMMAND,
/**
* Verification if the value for the given path is null.
*/
NULL;
public static boolean regexRelated(MatchingType type) {
return !type.equals(EQUALITY) && !type.equals(TYPE) && !type.equals(COMMAND)
&& !type.equals(NULL);
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
/**
* Matching type with corresponding values.
*/
public class MatchingTypeValue {
private MatchingType type;
/**
* Value to check.
*/
private Object value;
/**
* Min occurrence when matching by type.
*/
private Integer minTypeOccurrence;
/**
* Max occurrence when matching by type.
*/
private Integer maxTypeOccurrence;
MatchingTypeValue() {
}
MatchingTypeValue(MatchingType type) {
this.type = type;
}
MatchingTypeValue(MatchingType type, Object value) {
this.type = type;
this.value = value;
}
MatchingTypeValue(MatchingType type, Object value, Integer minTypeOccurrence) {
this.type = type;
this.value = value;
this.minTypeOccurrence = minTypeOccurrence;
}
MatchingTypeValue(MatchingType type, Object value, Integer minTypeOccurrence,
Integer maxTypeOccurrence) {
this.type = type;
this.value = value;
this.minTypeOccurrence = minTypeOccurrence;
this.maxTypeOccurrence = maxTypeOccurrence;
}
public MatchingType getType() {
return type;
}
public void setType(MatchingType type) {
this.type = type;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public Integer getMinTypeOccurrence() {
return minTypeOccurrence;
}
public void setMinTypeOccurrence(Integer minTypeOccurrence) {
this.minTypeOccurrence = minTypeOccurrence;
}
public Integer getMaxTypeOccurrence() {
return maxTypeOccurrence;
}
public void setMaxTypeOccurrence(Integer maxTypeOccurrence) {
this.maxTypeOccurrence = maxTypeOccurrence;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MatchingTypeValue value1 = (MatchingTypeValue) o;
return type == value1.type && Objects.equals(value, value1.value)
&& Objects.equals(minTypeOccurrence, value1.minTypeOccurrence)
&& Objects.equals(maxTypeOccurrence, value1.maxTypeOccurrence);
}
@Override
public int hashCode() {
return Objects.hash(type, value, minTypeOccurrence, maxTypeOccurrence);
}
@Override
public String toString() {
return "MatchingTypeValue{" + "type=" + type + ", value=" + value
+ ", minTypeOccurrence=" + minTypeOccurrence + ", maxTypeOccurrence="
+ maxTypeOccurrence + '}';
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
public class MatchingTypeValueHolder {
MatchingTypeValue matchingTypeValue = new MatchingTypeValue(MatchingType.TYPE);
public MatchingTypeValue minOccurrence(int number) {
this.matchingTypeValue.setMinTypeOccurrence(number);
return this.matchingTypeValue;
}
public MatchingTypeValue maxOccurrence(int number) {
this.matchingTypeValue.setMaxTypeOccurrence(number);
return this.matchingTypeValue;
}
public MatchingTypeValue occurrence(int number) {
this.matchingTypeValue.setMinTypeOccurrence(number);
this.matchingTypeValue.setMaxTypeOccurrence(number);
return this.matchingTypeValue;
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Contains most commonly used media types.
*
* @author Marcin Grzejszczak
* @since 1.0.2
*/
public class MediaTypes {
public String allValue() {
return "*/*";
}
public String applicationAtomXml() {
return "application/atom+xml";
}
public String applicationFormUrlencoded() {
return "application/x-www-form-urlencoded";
}
public String applicationJson() {
return "application/json";
}
public String applicationJsonUtf8() {
return applicationJson() + ";charset=UTF-8";
}
public String applicationOctetStream() {
return "application/octet-stream";
}
public String applicationPdf() {
return "application/pdf";
}
public String applicationXhtmlXml() {
return "application/xhtml+xml";
}
public String applicationXml() {
return "application/xml";
}
public String imageGif() {
return "image/gif";
}
public String imageJpeg() {
return "image/jpeg";
}
public String imagePng() {
return "image/png";
}
public String multipartFormData() {
return "multipart/form-data";
}
public String textHtml() {
return "text/html";
}
public String textMarkdown() {
return "text/markdown";
}
public String textPlain() {
return "text/plain";
}
public String textXml() {
return "text/xml";
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Contains most commonly used messaging headers.
*
* @author Marcin Grzejszczak
* @since 1.1.2
*/
public class MessagingHeaders {
/**
* The Content Type of a message.
* @return messaging content type
*/
public String messagingContentType() {
return "contentType";
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Multipart extends DslProperty {
public Multipart(Map<String, DslProperty> multipart) {
super(extractValue(multipart, ContractUtils.CLIENT_VALUE),
extractValue(multipart, ContractUtils.SERVER_VALUE));
}
public Multipart(List<DslProperty> multipartAsList) {
super(multipartAsList.stream().map(DslProperty::getClientValue)
.collect(Collectors.toList()),
multipartAsList.stream().map(DslProperty::getServerValue)
.collect(Collectors.toList()));
}
public Multipart(Object value) {
super(ContractUtils.CLIENT_VALUE.apply(value),
ContractUtils.SERVER_VALUE.apply(value));
}
public Multipart(DslProperty multipartAsValue) {
super(multipartAsValue.getClientValue(), multipartAsValue.getServerValue());
}
public Multipart(MatchingStrategy matchingStrategy) {
super(matchingStrategy);
}
public static Multipart build(Object value) {
if (value instanceof MatchingStrategy) {
return new Multipart((MatchingStrategy) value);
}
return new Multipart(value);
}
private static Map<String, Object> extractValue(Map<String, DslProperty> multipart,
final Function valueProvider) {
final Map<String, Object> map = new LinkedHashMap<String, Object>();
multipart.forEach(
(s, dslProperty) -> map.put(s, valueProvider.apply(dslProperty)));
return map;
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Map;
import java.util.Objects;
/**
* Represents a property that has name and content. Used together with multipart requests.
*
* @since 1.0.0
*/
public class NamedProperty {
private static final String NAME = "name";
private static final String CONTENT = "content";
private static final String CONTENT_TYPE = "contentType";
private DslProperty name;
private DslProperty value;
private DslProperty contentType;
public NamedProperty(DslProperty name, DslProperty value) {
this.name = name;
this.value = value;
this.contentType = null;
}
public NamedProperty(DslProperty name, DslProperty value, DslProperty contentType) {
this.name = name;
this.value = value;
this.contentType = contentType;
}
public NamedProperty(Map<String, DslProperty> namedMap) {
this(asDslProperty(value(namedMap, NAME)),
asDslProperty(value(namedMap, CONTENT)),
asDslProperty(value(namedMap, CONTENT_TYPE)));
}
private static DslProperty value(Map<String, DslProperty> namedMap, String key) {
if (namedMap == null) {
return null;
}
return namedMap.get(key);
}
public static DslProperty asDslProperty(Object o) {
if (o == null) {
return null;
}
if (o instanceof DslProperty) {
return ((DslProperty) (o));
}
return new DslProperty(o);
}
public DslProperty getName() {
return name;
}
public void setName(DslProperty name) {
this.name = name;
}
public DslProperty getValue() {
return value;
}
public void setValue(DslProperty value) {
this.value = value;
}
public DslProperty getContentType() {
return contentType;
}
public void setContentType(DslProperty contentType) {
this.contentType = contentType;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
NamedProperty that = (NamedProperty) o;
return Objects.equals(name, that.name) && Objects.equals(value, that.value)
&& Objects.equals(contentType, that.contentType);
}
@Override
public int hashCode() {
return Objects.hash(name, value, contentType);
}
@Override
public String toString() {
return "NamedProperty{" + "name=" + name + ", value=" + value + ", contentType="
+ contentType + '}';
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.regex.Pattern;
/**
* Special case of Patterns that we don't want to escape.
*
* @author Marcin Grzejszczak
* @since 1.0.3
*/
public class NotToEscapePattern extends DslProperty<Pattern> {
public NotToEscapePattern(Pattern clientValue, Pattern serverValue) {
super(clientValue, serverValue);
}
public NotToEscapePattern(Pattern singleValue) {
super(singleValue);
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.io.Serializable;
import java.util.regex.Pattern;
import repackaged.nl.flotsam.xeger.Xeger;
/**
* Represents a property that may or may not be there.
*
* @since 1.0.0
*/
public class OptionalProperty implements Serializable, CanBeDynamic {
private final Object value;
public OptionalProperty(Object value) {
this.value = value;
}
/**
* String version of a regular expression that wraps the provided value in an optional
* function.
* @return pattern wrapped in optional regular expresion
*/
public String optionalPattern() {
return "(" + value() + ")?";
}
public String value() {
return this.value instanceof RegexProperty
? ((RegexProperty) this.value).pattern() : this.value.toString();
}
protected Pattern optionalPatternValue() {
return Pattern.compile(optionalPattern());
}
@Override
public String toString() {
return optionalPattern();
}
@Override
public Object generateConcreteValue() {
return new Xeger(optionalPattern()).generate();
}
public final Object getValue() {
return value;
}
}

View File

@@ -0,0 +1,400 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Represents an output for messaging. Used for verifying the body and headers that are
* sent.
*
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @since 1.0.0
*/
public class OutputMessage extends Common
implements RegexCreatingProperty<ServerDslProperty> {
private static final Log log = LogFactory.getLog(OutputMessage.class);
private DslProperty<String> sentTo;
private Headers headers;
private DslProperty body;
private ExecutionProperty assertThat;
private ResponseBodyMatchers bodyMatchers;
private ServerPatternValueDslProperty property = new ServerPatternValueDslProperty();
public OutputMessage() {
}
public OutputMessage(OutputMessage outputMessage) {
this.sentTo = outputMessage.getSentTo();
this.headers = outputMessage.getHeaders();
this.body = outputMessage.getBody();
}
public void sentTo(String sentTo) {
this.sentTo = new DslProperty(sentTo);
}
public void sentTo(DslProperty sentTo) {
this.sentTo = sentTo;
}
public void body(Object bodyAsValue) {
this.body = new DslProperty(bodyAsValue);
}
public void body(DslProperty bodyAsValue) {
this.body = bodyAsValue;
}
public void assertThat(String assertThat) {
this.assertThat = new ExecutionProperty(assertThat);
}
/**
* @deprecated - use the server dsl property
* @param clientDslProperty client property
* @return dsl property
*/
@Deprecated
public DslProperty value(ClientDslProperty clientDslProperty) {
return value(new ServerDslProperty(clientDslProperty.getServerValue(),
clientDslProperty.getClientValue()));
}
public DslProperty value(ServerDslProperty serverDslProperty) {
Object concreteValue = serverDslProperty.getClientValue();
Object dynamicValue = serverDslProperty.getServerValue();
// for the output messages ran via stub runner,
// entries have to have fixed values
if (dynamicValue instanceof RegexProperty) {
return ((RegexProperty) dynamicValue).concreteClientEscapedDynamicProducer();
}
return new DslProperty(concreteValue, dynamicValue);
}
/**
* @deprecated - use the server dsl property
* @param client client value
* @return dsl proprty
*/
@Deprecated
public DslProperty $(ClientDslProperty client) {
return value(client);
}
public DslProperty $(ServerDslProperty property) {
return value(property);
}
public DslProperty $(Pattern pattern) {
return value(new RegexProperty(pattern));
}
public DslProperty $(RegexProperty pattern) {
return value(pattern);
}
public DslProperty value(RegexProperty pattern) {
return value(producer(pattern));
}
public DslProperty $(OptionalProperty property) {
return value(producer(property.optionalPatternValue()));
}
@Override
public RegexProperty regexProperty(Object object) {
return new RegexProperty(object).concreteClientDynamicProducer();
}
public ServerPatternValueDslProperty getProperty() {
return property;
}
public void setProperty(ServerPatternValueDslProperty property) {
this.property = property;
}
public DslProperty<String> getSentTo() {
return sentTo;
}
public void setSentTo(DslProperty<String> sentTo) {
this.sentTo = sentTo;
}
public Headers getHeaders() {
return headers;
}
public void setHeaders(Headers headers) {
this.headers = headers;
}
public DslProperty getBody() {
return body;
}
public void setBody(DslProperty body) {
this.body = body;
}
public ExecutionProperty getAssertThat() {
return assertThat;
}
public void setAssertThat(ExecutionProperty assertThat) {
this.assertThat = assertThat;
}
public ResponseBodyMatchers getBodyMatchers() {
return bodyMatchers;
}
public void setBodyMatchers(ResponseBodyMatchers bodyMatchers) {
this.bodyMatchers = bodyMatchers;
}
@Override
public ServerDslProperty anyAlphaUnicode() {
return property.anyAlphaUnicode();
}
@Override
public ServerDslProperty anyAlphaNumeric() {
return property.anyAlphaNumeric();
}
@Override
public ServerDslProperty anyNumber() {
return property.anyNumber();
}
@Override
public ServerDslProperty anyInteger() {
return property.anyInteger();
}
@Override
public ServerDslProperty anyPositiveInt() {
return property.anyPositiveInt();
}
@Override
public ServerDslProperty anyDouble() {
return property.anyDouble();
}
@Override
public ServerDslProperty anyHex() {
return property.anyHex();
}
@Override
public ServerDslProperty aBoolean() {
return property.aBoolean();
}
@Override
public ServerDslProperty anyIpAddress() {
return property.anyIpAddress();
}
@Override
public ServerDslProperty anyHostname() {
return property.anyHostname();
}
@Override
public ServerDslProperty anyEmail() {
return property.anyEmail();
}
@Override
public ServerDslProperty anyUrl() {
return property.anyUrl();
}
@Override
public ServerDslProperty anyHttpsUrl() {
return property.anyHttpsUrl();
}
@Override
public ServerDslProperty anyUuid() {
return property.anyUuid();
}
@Override
public ServerDslProperty anyDate() {
return property.anyDate();
}
@Override
public ServerDslProperty anyDateTime() {
return property.anyDateTime();
}
@Override
public ServerDslProperty anyTime() {
return property.anyTime();
}
@Override
public ServerDslProperty anyIso8601WithOffset() {
return property.anyIso8601WithOffset();
}
@Override
public ServerDslProperty anyNonBlankString() {
return property.anyNonBlankString();
}
@Override
public ServerDslProperty anyNonEmptyString() {
return property.anyNonEmptyString();
}
@Override
public ServerDslProperty anyOf(String... values) {
return property.anyOf(values);
}
/**
* The message headers part of the contract.
* @param consumer function to manipulate the message headers
*/
public void headers(Consumer<Headers> consumer) {
this.headers = new Headers();
consumer.accept(this.headers);
}
/**
* The message headers part of the contract.
* @param consumer function to manipulate the message headers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
@Deprecated
public void testMatchers(Consumer<ResponseBodyMatchers> consumer) {
log.warn("testMatchers method is deprecated. Please use bodyMatchers instead");
bodyMatchers(consumer);
}
/**
* The stub matchers part of the contract.
* @param consumer function to manipulate the body matchers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
@Deprecated
public void bodyMatchers(Consumer<ResponseBodyMatchers> consumer) {
this.bodyMatchers = new ResponseBodyMatchers();
consumer.accept(this.bodyMatchers);
}
/**
* The message headers part of the contract.
* @param consumer function to manipulate the message headers
*/
public void headers(@DelegatesTo(Headers.class) Closure consumer) {
this.headers = new Headers();
consumer.setDelegate(this.headers);
consumer.call();
}
/**
* The message headers part of the contract.
* @param consumer function to manipulate the message headers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
@Deprecated
public void testMatchers(@DelegatesTo(ResponseBodyMatchers.class) Closure consumer) {
log.warn("testMatchers method is deprecated. Please use bodyMatchers instead");
bodyMatchers(consumer);
}
/**
* The stub matchers part of the contract.
* @param consumer function to manipulate the body matchers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
@Deprecated
public void bodyMatchers(@DelegatesTo(ResponseBodyMatchers.class) Closure consumer) {
this.bodyMatchers = new ResponseBodyMatchers();
consumer.setDelegate(this.bodyMatchers);
consumer.call();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OutputMessage that = (OutputMessage) o;
return Objects.equals(sentTo, that.sentTo)
&& Objects.equals(headers, that.headers)
&& Objects.equals(body, that.body)
&& Objects.equals(assertThat, that.assertThat)
&& Objects.equals(bodyMatchers, that.bodyMatchers);
}
@Override
public int hashCode() {
return Objects.hash(sentTo, headers, body, assertThat, bodyMatchers);
}
@Override
public String toString() {
return "OutputMessage{" + "\n\tsentTo=" + sentTo + ", \n\theaders=" + headers
+ ", \n\tbody=" + body + ", \n\tassertThat=" + assertThat
+ ", \n\tbodyMatchers=" + bodyMatchers + "} \n\t" + super.toString();
}
private class ServerPatternValueDslProperty
extends PatternValueDslProperty<ServerDslProperty> {
@Override
protected ServerDslProperty createProperty(Pattern pattern,
Object generatedValue) {
return new ServerDslProperty(pattern, generatedValue);
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
/**
* @author Marcin Grzejszczak
*/
public class PathBodyMatcher implements BodyMatcher {
private String path;
private MatchingTypeValue matchingTypeValue;
PathBodyMatcher(String path, MatchingTypeValue matchingTypeValue) {
this.path = path;
this.matchingTypeValue = matchingTypeValue;
}
@Override
public MatchingType matchingType() {
return this.matchingTypeValue.getType();
}
@Override
public String path() {
return this.path;
}
@Override
public Object value() {
return this.matchingTypeValue.getValue();
}
@Override
public Integer minTypeOccurrence() {
return this.matchingTypeValue.getMinTypeOccurrence();
}
@Override
public Integer maxTypeOccurrence() {
return this.matchingTypeValue.getMaxTypeOccurrence();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PathBodyMatcher that = (PathBodyMatcher) o;
return Objects.equals(path, that.path)
&& Objects.equals(matchingTypeValue, that.matchingTypeValue);
}
@Override
public int hashCode() {
return Objects.hash(path, matchingTypeValue);
}
@Override
public String toString() {
return "PathBodyMatcher{" + "path='" + path + '\'' + ", matchingTypeValue="
+ matchingTypeValue + '}';
}
}

View File

@@ -0,0 +1,190 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Random;
import java.util.UUID;
import java.util.regex.Pattern;
import org.apache.commons.lang3.RandomStringUtils;
/**
* @author Marcin Grzejszczak
* @author Tim Ysewyn
*/
abstract class PatternValueDslProperty<T extends DslProperty>
implements RegexCreatingProperty<T> {
private final Random random = new Random();
protected T createAndValidateProperty(Pattern pattern, Object object) {
if (object != null) {
String generatedValue = object.toString();
boolean matches = pattern.matcher(generatedValue).matches();
if (!matches) {
throw new IllegalStateException("The generated value [" + generatedValue
+ "] doesn\'t match the pattern [" + pattern.pattern() + "]");
}
return createProperty(pattern, object);
}
return createProperty(pattern, object);
}
protected T createAndValidateProperty(Pattern pattern) {
return createAndValidateProperty(pattern, null);
}
/**
* Method to generate the PatternValue. The resulting implementation will create
* either a Client or a Server side impl.
* @param pattern - pattern for which the value will be generated or reused
* @param generatedValue - Nullable - potential generated value to be reused
* @return {@link DslProperty} wrapping a pattern and generated value
*/
protected abstract T createProperty(Pattern pattern, Object generatedValue);
@Override
public T anyAlphaUnicode() {
return createAndValidateProperty(RegexPatterns.ONLY_ALPHA_UNICODE,
RandomStringGenerator.randomString(20));
}
@Override
public T anyAlphaNumeric() {
return createAndValidateProperty(RegexPatterns.ALPHA_NUMERIC,
RandomStringUtils.randomAlphanumeric(20));
}
@Override
public T anyNumber() {
return createAndValidateProperty(RegexPatterns.NUMBER, this.random.nextInt());
}
@Override
public T anyInteger() {
return createAndValidateProperty(RegexPatterns.INTEGER, this.random.nextInt());
}
@Override
public T anyPositiveInt() {
return createAndValidateProperty(RegexPatterns.POSITIVE_INT,
Math.abs(this.random.nextInt() + 1));
}
@Override
public T anyDouble() {
return createAndValidateProperty(RegexPatterns.DOUBLE,
this.random.nextInt(100) + this.random.nextDouble());
}
@Override
public T anyHex() {
return createAndValidateProperty(RegexPatterns.HEX,
RandomStringUtils.random(10, "0123456789abcdef"));
}
@Override
public T aBoolean() {
return createAndValidateProperty(RegexPatterns.TRUE_OR_FALSE);
}
@Override
public T anyIpAddress() {
return createAndValidateProperty(RegexPatterns.IP_ADDRESS,
"192.168.0." + this.random.nextInt(10));
}
@Override
public T anyHostname() {
return createAndValidateProperty(RegexPatterns.HOSTNAME_PATTERN,
"https://foo" + this.random.nextInt() + ".com");
}
@Override
public T anyEmail() {
return createAndValidateProperty(RegexPatterns.EMAIL,
"foo@bar" + this.random.nextInt() + ".com");
}
@Override
public T anyUrl() {
return createAndValidateProperty(RegexPatterns.URL,
"https://foo" + this.random.nextInt() + ".com");
}
@Override
public T anyHttpsUrl() {
return createAndValidateProperty(RegexPatterns.HTTPS_URL,
"https://baz" + this.random.nextInt() + ".com");
}
@Override
public T anyUuid() {
return createAndValidateProperty(RegexPatterns.UUID,
UUID.randomUUID().toString());
}
@Override
public T anyDate() {
int d = this.random.nextInt(8) + 1;
return createAndValidateProperty(RegexPatterns.ANY_DATE, "201" + String.valueOf(d)
+ "-0" + String.valueOf(d) + "-1" + String.valueOf(d));
}
@Override
public T anyDateTime() {
final int d = this.random.nextInt(8) + 1;
return createAndValidateProperty(RegexPatterns.ANY_DATE_TIME,
"201" + String.valueOf(d) + "-0" + String.valueOf(d) + "-1"
+ String.valueOf(d) + "T12:23:34");
}
@Override
public T anyTime() {
int d = this.random.nextInt(9);
return createAndValidateProperty(RegexPatterns.ANY_TIME,
"12:2" + String.valueOf(d) + ":3" + String.valueOf(d));
}
@Override
public T anyIso8601WithOffset() {
final int d = this.random.nextInt(8) + 1;
return createAndValidateProperty(RegexPatterns.ISO8601_WITH_OFFSET,
"201" + String.valueOf(d) + "-0" + String.valueOf(d) + "-1"
+ String.valueOf(d) + "T12:23:34.123Z");
}
@Override
public T anyNonBlankString() {
return createAndValidateProperty(RegexPatterns.NON_BLANK,
RandomStringGenerator.randomString(20));
}
@Override
public T anyNonEmptyString() {
return createAndValidateProperty(RegexPatterns.NON_EMPTY,
RandomStringGenerator.randomString(20));
}
@Override
public T anyOf(String... values) {
return createAndValidateProperty(RegexPatterns.anyOf(values), values[0]);
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
import org.springframework.cloud.contract.spec.util.ValidateUtils;
/**
* Represents a single HTTP query parameter.
*
* @since 1.0.0
*/
public class QueryParameter extends DslProperty {
private String name;
public QueryParameter(String name, DslProperty dslProperty) {
super(dslProperty.getClientValue(), dslProperty.getServerValue());
ValidateUtils.validateServerValueIsAvailable(dslProperty.getServerValue(),
"Query parameter \'" + name + "\'");
this.name = name;
}
public QueryParameter(String name, MatchingStrategy matchingStrategy) {
super(matchingStrategy);
ValidateUtils.validateServerValueIsAvailable(matchingStrategy,
"Query parameter \'" + name + "\'");
this.name = name;
}
public QueryParameter(String name, Object value) {
super(ContractUtils.CLIENT_VALUE.apply(value),
ContractUtils.SERVER_VALUE.apply(value));
ValidateUtils.validateServerValueIsAvailable(value,
"Query parameter \'" + name + "\'");
this.name = name;
}
public static QueryParameter build(String key, Object value) {
if (value instanceof MatchingStrategy) {
return new QueryParameter(key, (MatchingStrategy) value);
}
else if (value instanceof RegexProperty) {
return new QueryParameter(key,
((RegexProperty) value).dynamicClientEscapedConcreteProducer());
}
return new QueryParameter(key, value);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
QueryParameter that = (QueryParameter) o;
return Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), name);
}
@Override
public String toString() {
return "QueryParameter{" + "name='" + name + '\'' + ", value=" + super.toString()
+ '}';
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class QueryParameters {
private List<QueryParameter> parameters = new LinkedList<QueryParameter>();
public void parameter(Map<String, Object> singleParameter) {
Iterator<Map.Entry<String, Object>> iterator = singleParameter.entrySet()
.iterator();
if (iterator.hasNext()) {
Map.Entry<String, Object> first = iterator.next();
if (first != null) {
parameters.add(QueryParameter.build(first.getKey(), first.getValue()));
}
}
}
public void parameter(String parameterName, Object parameterValue) {
parameters.add(QueryParameter.build(parameterName, parameterValue));
}
public List<QueryParameter> getParameters() {
return parameters;
}
public void setParameters(List<QueryParameter> parameters) {
this.parameters = parameters;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
QueryParameters that = (QueryParameters) o;
return Objects.equals(parameters, that.parameters);
}
@Override
public int hashCode() {
return Objects.hash(parameters);
}
@Override
public String toString() {
return "QueryParameters{" + "\nparameters=" + parameters + '}';
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Random;
final class RandomStringGenerator {
private RandomStringGenerator() {
throw new IllegalStateException("Can't instantiate a utility class");
}
static String randomString(int length) {
char[] characterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
Random random = new Random();
char[] result = new char[length];
for (int i = 0; i < result.length; i++) {
// picks a random index out of character set > random character
int randomCharIndex = random.nextInt(characterSet.length);
result[i] = characterSet[randomCharIndex];
}
return new String(result);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* @author Marcin Grzejszczak
*/
interface RegexCreatingProperty<T extends DslProperty> {
T anyAlphaUnicode();
T anyAlphaNumeric();
T anyNumber();
T anyInteger();
T anyPositiveInt();
T anyDouble();
T anyHex();
T aBoolean();
T anyIpAddress();
T anyHostname();
T anyEmail();
T anyUrl();
T anyHttpsUrl();
T anyUuid();
T anyDate();
T anyDateTime();
T anyTime();
T anyIso8601WithOffset();
T anyNonBlankString();
T anyNonEmptyString();
T anyOf(String... values);
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Matching type with corresponding values.
*/
public class RegexMatchingTypeValue extends MatchingTypeValue {
RegexMatchingTypeValue(MatchingType type, Object value, Integer minTypeOccurrence,
Integer maxTypeOccurrence) {
super(type, value, minTypeOccurrence, maxTypeOccurrence);
}
RegexMatchingTypeValue(MatchingType type, Object value) {
super(type, value);
}
public RegexMatchingTypeValue asInteger() {
return typed(Integer.class);
}
private RegexMatchingTypeValue typed(Class clazz) {
if (!(this.getValue() instanceof RegexProperty)) {
throw new IllegalStateException("Value has to be a regex");
}
RegexProperty regexProperty = (RegexProperty) this.getValue();
return new RegexMatchingTypeValue(this.getType(),
new RegexProperty(regexProperty.getClientValue(),
regexProperty.getServerValue(), clazz),
this.getMinTypeOccurrence(), this.getMaxTypeOccurrence());
}
public RegexMatchingTypeValue asDouble() {
return typed(Double.class);
}
public RegexMatchingTypeValue asFloat() {
return typed(Float.class);
}
public RegexMatchingTypeValue asLong() {
return typed(Long.class);
}
public RegexMatchingTypeValue asShort() {
return typed(Short.class);
}
public RegexMatchingTypeValue asString() {
return typed(String.class);
}
public RegexMatchingTypeValue asBooleanType() {
return typed(Boolean.class);
}
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Arrays;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Contains most common regular expression patterns.
*
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @since 1.0.0
*/
public class RegexPatterns {
// tag::regexps[]
protected static final Pattern TRUE_OR_FALSE = Pattern.compile("(true|false)");
protected static final Pattern ALPHA_NUMERIC = Pattern.compile("[a-zA-Z0-9]+");
protected static final Pattern ONLY_ALPHA_UNICODE = Pattern.compile("[\\p{L}]*");
protected static final Pattern NUMBER = Pattern.compile("-?(\\d*\\.\\d+|\\d+)");
protected static final Pattern INTEGER = Pattern.compile("-?(\\d+)");
protected static final Pattern POSITIVE_INT = Pattern.compile("([1-9]\\d*)");
protected static final Pattern DOUBLE = Pattern.compile("-?(\\d*\\.\\d+)");
protected static final Pattern HEX = Pattern.compile("[a-fA-F0-9]+");
protected static final Pattern IP_ADDRESS = Pattern.compile(
"([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])");
protected static final Pattern HOSTNAME_PATTERN = Pattern
.compile("((http[s]?|ftp):/)/?([^:/\\s]+)(:[0-9]{1,5})?");
protected static final Pattern EMAIL = Pattern
.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}");
protected static final Pattern URL = UrlHelper.URL;
protected static final Pattern HTTPS_URL = UrlHelper.HTTPS_URL;
protected static final Pattern UUID = Pattern
.compile("[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}");
protected static final Pattern ANY_DATE = Pattern
.compile("(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])");
protected static final Pattern ANY_DATE_TIME = Pattern.compile(
"([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])");
protected static final Pattern ANY_TIME = Pattern
.compile("(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])");
protected static final Pattern NON_EMPTY = Pattern.compile("[\\S\\s]+");
protected static final Pattern NON_BLANK = Pattern.compile("^\\s*\\S[\\S\\s]*");
protected static final Pattern ISO8601_WITH_OFFSET = Pattern.compile(
"([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\\.\\d{3})?(Z|[+-][01]\\d:[0-5]\\d)");
protected static Pattern anyOf(String... values) {
return Pattern.compile(Arrays.stream(values).map(it -> '^' + it + '$')
.collect(Collectors.joining("|")));
}
public static String multipartParam(Object name, Object value) {
return ".*--(.*)\r\nContent-Disposition: form-data; name=\"" + name
+ "\"\r\n(Content-Type: .*\r\n)?(Content-Transfer-Encoding: .*\r\n)?(Content-Length: \\d+\r\n)?\r\n"
+ value + "\r\n--\\1.*";
}
public static String multipartFile(Object name, Object filename, Object content,
Object contentType) {
return ".*--(.*)\r\nContent-Disposition: form-data; name=\"" + name
+ "\"; filename=\"" + filename + "\"\r\n(Content-Type: "
+ toContentType(contentType)
+ "\r\n)?(Content-Transfer-Encoding: .*\r\n)?(Content-Length: \\d+\r\n)?\r\n"
+ content + "\r\n--\\1.*";
}
private static String toContentType(Object contentType) {
if (contentType == null) {
return ".*";
}
if (contentType instanceof RegexProperty) {
return ((RegexProperty) contentType).pattern();
}
return contentType.toString();
}
public RegexProperty onlyAlphaUnicode() {
return new RegexProperty(ONLY_ALPHA_UNICODE).asString();
}
public RegexProperty alphaNumeric() {
return new RegexProperty(ALPHA_NUMERIC).asString();
}
public RegexProperty number() {
return new RegexProperty(NUMBER).asDouble();
}
public RegexProperty positiveInt() {
return new RegexProperty(POSITIVE_INT).asInteger();
}
public RegexProperty anyBoolean() {
return new RegexProperty(TRUE_OR_FALSE).asBooleanType();
}
public RegexProperty anInteger() {
return new RegexProperty(INTEGER).asInteger();
}
public RegexProperty aDouble() {
return new RegexProperty(DOUBLE).asDouble();
}
public RegexProperty ipAddress() {
return new RegexProperty(IP_ADDRESS).asString();
}
public RegexProperty hostname() {
return new RegexProperty(HOSTNAME_PATTERN).asString();
}
public RegexProperty email() {
return new RegexProperty(EMAIL).asString();
}
public RegexProperty url() {
return new RegexProperty(URL).asString();
}
public RegexProperty httpsUrl() {
return new RegexProperty(HTTPS_URL).asString();
}
public RegexProperty uuid() {
return new RegexProperty(UUID).asString();
}
public RegexProperty isoDate() {
return new RegexProperty(ANY_DATE).asString();
}
public RegexProperty isoDateTime() {
return new RegexProperty(ANY_DATE_TIME).asString();
}
public RegexProperty isoTime() {
return new RegexProperty(ANY_TIME).asString();
}
// end::regexps[]
public RegexProperty iso8601WithOffset() {
return new RegexProperty(ISO8601_WITH_OFFSET).asString();
}
public RegexProperty nonEmpty() {
return new RegexProperty(NON_EMPTY).asString();
}
public RegexProperty nonBlank() {
return new RegexProperty(NON_BLANK).asString();
}
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.text.StringEscapeUtils;
import repackaged.nl.flotsam.xeger.Xeger;
/**
* Represents a regular expression property.
*
* @since 2.1.0
*/
public class RegexProperty extends DslProperty implements CanBeDynamic {
final Pattern pattern;
private final Class clazz;
public RegexProperty(Object value) {
this(value, value, null);
}
public RegexProperty(Object client, Object server) {
this(client, server, null);
}
public RegexProperty(Object client, Object server, Class clazz) {
super(client, server);
boolean clientDynamic = client instanceof Pattern
|| client instanceof RegexProperty;
boolean serverDynamic = server instanceof Pattern
|| server instanceof RegexProperty;
if (!clientDynamic && !serverDynamic) {
throw new IllegalStateException("Neither client not server side is dynamic");
}
Object dynamicValue = clientDynamic ? client : server;
if (dynamicValue instanceof Pattern) {
this.pattern = (Pattern) dynamicValue;
this.clazz = clazz != null ? clazz : String.class;
}
else if (dynamicValue instanceof RegexProperty) {
RegexProperty regexProperty = ((RegexProperty) dynamicValue);
this.pattern = regexProperty.pattern;
this.clazz = clazz != null ? clazz : regexProperty.clazz;
}
else {
this.clazz = clazz;
this.pattern = null;
}
}
public Matcher matcher(CharSequence input) {
return this.pattern.matcher(input);
}
public String pattern() {
return this.pattern.pattern();
}
public Class clazz() {
return this.getClass();
}
public RegexProperty asInteger() {
return new RegexProperty(this.getClientValue(), this.getServerValue(),
Integer.class);
}
public RegexProperty asDouble() {
return new RegexProperty(this.getClientValue(), this.getServerValue(),
Double.class);
}
public RegexProperty asFloat() {
return new RegexProperty(this.getClientValue(), this.getServerValue(),
Float.class);
}
public RegexProperty asLong() {
return new RegexProperty(this.getClientValue(), this.getServerValue(),
Long.class);
}
public RegexProperty asShort() {
return new RegexProperty(this.getClientValue(), this.getServerValue(),
Short.class);
}
public RegexProperty asString() {
return new RegexProperty(this.getClientValue(), this.getServerValue(),
String.class);
}
public RegexProperty asBooleanType() {
return new RegexProperty(this.getClientValue(), this.getServerValue(),
Boolean.class);
}
public Object generate() {
return doGenerate(3);
}
private Object doGenerate(int retries) {
try {
String generatedValue = new Xeger(this.pattern.pattern()).generate();
if (Integer.class.equals(this.clazz)) {
return Integer.parseInt(generatedValue);
}
else if (Double.class.equals(this.clazz)) {
return Double.parseDouble(generatedValue);
}
else if (Float.class.equals(this.clazz)) {
return Float.parseFloat(generatedValue);
}
else if (Long.class.equals(this.clazz)) {
return Long.parseLong(generatedValue);
}
else if (Short.class.equals(this.clazz)) {
return Short.parseShort(generatedValue);
}
else if (Boolean.class.equals(this.clazz)) {
return Boolean.parseBoolean(generatedValue);
}
return generatedValue;
}
catch (NumberFormatException ex) {
if (retries > 0) {
retries = retries - 1;
return doGenerate(retries);
}
throw ex;
}
}
public Object generateAndEscapeJavaStringIfNeeded() {
Object generated = generate();
if (isNumber()) {
return generated;
}
return StringEscapeUtils.escapeJava(String.valueOf(generated));
}
private boolean isNumber() {
return Number.class.isAssignableFrom(this.clazz);
}
public RegexProperty dynamicClientConcreteProducer() {
return new RegexProperty(this.pattern, generate(), this.clazz);
}
public RegexProperty concreteClientDynamicProducer() {
return new RegexProperty(generate(), this.pattern, this.clazz);
}
public RegexProperty concreteClientEscapedDynamicProducer() {
return new RegexProperty(generateAndEscapeJavaStringIfNeeded(), this.pattern,
this.clazz);
}
public RegexProperty dynamicClientEscapedConcreteProducer() {
return new RegexProperty(this.pattern, generateAndEscapeJavaStringIfNeeded(),
this.clazz);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RegexProperty that = (RegexProperty) o;
return Objects.equals(stringPatternIfPresent(pattern),
stringPatternIfPresent(that.pattern))
&& Objects.equals(clazz, that.clazz);
}
private Object stringPatternIfPresent(Pattern value) {
return value != null ? value.pattern() : null;
}
@Override
public int hashCode() {
return Objects.hash(stringPatternIfPresent(pattern), clazz);
}
@Override
public String toString() {
return this.pattern();
}
@Override
public Object generateConcreteValue() {
return generate();
}
public Pattern getPattern() {
return pattern;
}
public Class getClazz() {
return clazz;
}
}

View File

@@ -0,0 +1,848 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.contract.spec.util.RegexpUtils;
/**
* Represents the request side of the HTTP communication.
*
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @since 1.0.0
*/
public class Request extends Common implements RegexCreatingProperty<ClientDslProperty> {
private static final Log log = LogFactory.getLog(Request.class);
private ClientPatternValueDslProperty property = new ClientPatternValueDslProperty();
private HttpMethods httpMethods = new HttpMethods();
private DslProperty method;
private Url url;
private UrlPath urlPath;
private Headers headers;
private Cookies cookies;
private Body body;
private Multipart multipart;
private BodyMatchers bodyMatchers;
public Request() {
}
public Request(Request request) {
this.method = request.getMethod();
this.url = request.getUrl();
this.urlPath = request.getUrlPath();
this.headers = request.getHeaders();
this.cookies = request.getCookies();
this.body = request.getBody();
this.multipart = request.getMultipart();
}
/**
* Name of the HTTP method.
* @param method HTTP method name
*/
public void method(String method) {
this.method = toDslProperty(method);
}
/**
* Name of the HTTP method.
* @param httpMethod HTTP method name
*/
public void method(HttpMethods.HttpMethod httpMethod) {
this.method = toDslProperty(httpMethod.toString());
}
/**
* Name of the HTTP method.
* @param method HTTP method name
*/
public void method(DslProperty method) {
this.method = toDslProperty(method);
}
/**
* @param url URL to which the request will be sent
*/
public void url(Object url) {
this.url = new Url(url);
}
/**
* @param url URL to which the request will be sent
*/
public void url(DslProperty url) {
this.url = new Url(url);
}
/**
* @param path URL to which the request will be sent
*/
public void urlPath(String path) {
this.urlPath = new UrlPath(path);
}
/**
* @param path URL to which the request will be sent
*/
public void urlPath(Object path) {
this.urlPath = new UrlPath(path);
}
/**
* @param path URL to which the request will be sent
*/
public void urlPath(DslProperty path) {
this.urlPath = new UrlPath(path);
}
/**
* Allows set an HTTP body.
* @param body body to set
*/
public void body(Map<String, Object> body) {
this.body = new Body(convertObjectsToDslProperties(body));
}
/**
* Allows set an HTTP body.
* @param body body to set
*/
public void body(List body) {
this.body = new Body(convertObjectsToDslProperties(body));
}
/**
* Allows set an HTTP body.
* @param matchingStrategy body to set
*/
public void body(MatchingStrategy matchingStrategy) {
this.body = new Body(matchingStrategy);
}
/**
* Allows set an HTTP body.
* @param dslProperty body to set
*/
public void body(DslProperty dslProperty) {
this.body = new Body(dslProperty);
}
/**
* Allows set an HTTP body.
* @param bodyAsValue body to set
*/
public void body(Object bodyAsValue) {
this.body = new Body(bodyAsValue);
}
public Body getBody() {
return body;
}
public void setBody(Body body) {
this.body = body;
}
/**
* Allows to set multipart via the map notation.
* @param body multipart in a map notation
*/
public void multipart(Map<String, Object> body) {
this.multipart = new Multipart(convertObjectsToDslProperties(body));
}
/**
* Allows to set multipart via lists.
* @param multipartAsList multipart in a list notation
*/
public void multipart(List multipartAsList) {
this.multipart = Multipart.build(convertObjectsToDslProperties(multipartAsList));
}
/**
* Allows to set multipart value.
* @param dslProperty multipart
*/
public void multipart(DslProperty dslProperty) {
this.multipart = new Multipart(dslProperty);
}
/**
* Allows to set multipart value.
* @param multipartAsValue multipart
*/
public void multipart(Object multipartAsValue) {
this.multipart = Multipart.build(multipartAsValue);
}
/**
* Sets the equality check to the given query parameter.
* @param value value to check against
* @return matching strategy
*/
public MatchingStrategy equalTo(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO);
}
/**
* Sets the containing check to the given query parameter.
* @param value value to check against
* @return matching strategy
*/
public MatchingStrategy containing(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.CONTAINS);
}
/**
* Sets the matching check to the given query parameter.
* @param value value to check against
* @return matching strategy
*/
public MatchingStrategy matching(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.MATCHING);
}
/**
* Sets the not matching check to the given query parameter.
* @param value value to check against
* @return matching strategy
*/
public MatchingStrategy notMatching(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.NOT_MATCHING);
}
/**
* Sets the XML equality check to the body.
* @param value value to check against
* @return matching strategy
*/
public MatchingStrategy equalToXml(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_XML);
}
/**
* Sets the JSON equality check to the body.
* @param value value to check against
* @return matching strategy
*/
public MatchingStrategy equalToJson(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON);
}
/**
* Sets absence check to the given query parameter.
* @return matching strategy
*/
public MatchingStrategy absent() {
return new MatchingStrategy(true, MatchingStrategy.Type.ABSENT);
}
@Override
public void assertThatSidesMatch(Object stubSide, Object testSide) {
if (testSide instanceof OptionalProperty) {
throw new IllegalStateException(
"Optional can be used only for the stub side of the request!");
}
super.assertThatSidesMatch(stubSide, testSide);
}
/**
* Allows to set a dynamic value for the given element.
* @param client client value
* @return dsl property
*/
public DslProperty value(ClientDslProperty client) {
Object concreteValue = client.getServerValue();
Object dynamicValue = client.getClientValue();
if (dynamicValue instanceof RegexProperty && client.isSingleValue()) {
return ((RegexProperty) dynamicValue).dynamicClientEscapedConcreteProducer();
}
else if (concreteValue instanceof RegexProperty && !client.isSingleValue()) {
concreteValue = dynamicValue;
}
return new DslProperty(dynamicValue, concreteValue);
}
/**
* Allows to set a dynamic value for the given regular expression element.
* @param property property to set
* @return dsl property
*/
public DslProperty $(RegexProperty property) {
return value(property);
}
/**
* Allows to set a dynamic value for the given regular expression element.
* @param property property to set
* @return dsl property
*/
public DslProperty value(RegexProperty property) {
return value(client(property));
}
/**
* Allows to set a dynamic value for the given element.
* @param client property to set
* @return dsl property
*/
public DslProperty $(ClientDslProperty client) {
return value(client);
}
/**
* Allows to set a dynamic value for the Pattern element.
* @param client property to set
* @return dsl property
*/
public DslProperty value(Pattern client) {
return value(new RegexProperty(client));
}
/**
* Allows to set a dynamic value for the given Pattern element.
* @param client property to set
* @return dsl property
*/
public DslProperty $(Pattern client) {
return value(client);
}
@Override
public RegexProperty regexProperty(Object object) {
return new RegexProperty(object).dynamicClientConcreteProducer();
}
/**
* Allows to set a dynamic value for client and server side.
* @param client client value
* @param server server value
* @return dsl property
*/
@Override
public DslProperty value(ClientDslProperty client, ServerDslProperty server) {
if (server.getClientValue() instanceof RegexProperty) {
throw new IllegalStateException(
"You can't have a regular expression for the request on the server side");
}
return super.value(client, server);
}
/**
* Allows to set a dynamic value for client and server side.
* @param client client value
* @param server server value
* @return dsl property
*/
@Override
public DslProperty value(ServerDslProperty server, ClientDslProperty client) {
if (server.getClientValue() instanceof RegexProperty) {
throw new IllegalStateException(
"You can't have a regular expression for the request on the server side");
}
return super.value(server, client);
}
public ClientPatternValueDslProperty getProperty() {
return property;
}
public void setProperty(ClientPatternValueDslProperty property) {
this.property = property;
}
public HttpMethods getHttpMethods() {
return httpMethods;
}
public void setHttpMethods(HttpMethods httpMethods) {
this.httpMethods = httpMethods;
}
public DslProperty getMethod() {
return method;
}
public void setMethod(DslProperty method) {
this.method = method;
}
public Url getUrl() {
return url;
}
public void setUrl(Url url) {
this.url = url;
}
public UrlPath getUrlPath() {
return urlPath;
}
public void setUrlPath(UrlPath urlPath) {
this.urlPath = urlPath;
}
public Headers getHeaders() {
return headers;
}
public void setHeaders(Headers headers) {
this.headers = headers;
}
public Cookies getCookies() {
return cookies;
}
public void setCookies(Cookies cookies) {
this.cookies = cookies;
}
public Multipart getMultipart() {
return multipart;
}
public void setMultipart(Multipart multipart) {
this.multipart = multipart;
}
public BodyMatchers getBodyMatchers() {
return bodyMatchers;
}
public void setBodyMatchers(BodyMatchers bodyMatchers) {
this.bodyMatchers = bodyMatchers;
}
@Override
public ClientDslProperty anyAlphaUnicode() {
return property.anyAlphaUnicode();
}
@Override
public ClientDslProperty anyAlphaNumeric() {
return property.anyAlphaNumeric();
}
@Override
public ClientDslProperty anyNumber() {
return property.anyNumber();
}
@Override
public ClientDslProperty anyInteger() {
return property.anyInteger();
}
@Override
public ClientDslProperty anyPositiveInt() {
return property.anyPositiveInt();
}
@Override
public ClientDslProperty anyDouble() {
return property.anyDouble();
}
@Override
public ClientDslProperty anyHex() {
return property.anyHex();
}
@Override
public ClientDslProperty aBoolean() {
return property.aBoolean();
}
@Override
public ClientDslProperty anyIpAddress() {
return property.anyIpAddress();
}
@Override
public ClientDslProperty anyHostname() {
return property.anyHostname();
}
@Override
public ClientDslProperty anyEmail() {
return property.anyEmail();
}
@Override
public ClientDslProperty anyUrl() {
return property.anyUrl();
}
@Override
public ClientDslProperty anyHttpsUrl() {
return property.anyHttpsUrl();
}
@Override
public ClientDslProperty anyUuid() {
return property.anyUuid();
}
@Override
public ClientDslProperty anyDate() {
return property.anyDate();
}
@Override
public ClientDslProperty anyDateTime() {
return property.anyDateTime();
}
@Override
public ClientDslProperty anyTime() {
return property.anyTime();
}
@Override
public ClientDslProperty anyIso8601WithOffset() {
return property.anyIso8601WithOffset();
}
@Override
public ClientDslProperty anyNonBlankString() {
return property.anyNonBlankString();
}
@Override
public ClientDslProperty anyNonEmptyString() {
return property.anyNonEmptyString();
}
@Override
public ClientDslProperty anyOf(String... values) {
return property.anyOf(values);
}
public HttpMethods.HttpMethod GET() {
return httpMethods.GET();
}
public HttpMethods.HttpMethod HEAD() {
return httpMethods.HEAD();
}
public HttpMethods.HttpMethod POST() {
return httpMethods.POST();
}
public HttpMethods.HttpMethod PUT() {
return httpMethods.PUT();
}
public HttpMethods.HttpMethod PATCH() {
return httpMethods.PATCH();
}
public HttpMethods.HttpMethod DELETE() {
return httpMethods.DELETE();
}
public HttpMethods.HttpMethod OPTIONS() {
return httpMethods.OPTIONS();
}
public HttpMethods.HttpMethod TRACE() {
return httpMethods.TRACE();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Request request = (Request) o;
return Objects.equals(method, request.method) && Objects.equals(url, request.url)
&& Objects.equals(urlPath, request.urlPath)
&& Objects.equals(headers, request.headers)
&& Objects.equals(cookies, request.cookies)
&& Objects.equals(body, request.body)
&& Objects.equals(multipart, request.multipart)
&& Objects.equals(bodyMatchers, request.bodyMatchers);
}
@Override
public int hashCode() {
return Objects.hash(method, url, urlPath, headers, cookies, body, multipart,
bodyMatchers);
}
@Override
public String toString() {
return "Request{" + "\nmethod=" + method + ", \n\turl=" + url + ", \n\turlPath="
+ urlPath + ", \n\theaders=" + headers + ", \n\tcookies=" + cookies
+ ", \n\tbody=" + body + ", \n\tmultipart=" + multipart
+ ", \n\tbodyMatchers=" + bodyMatchers + '}';
}
/**
* The URL of the contract.
* @param url url value
* @param consumer function to manipulate the URL
*/
public void url(Object url, Consumer<Url> consumer) {
this.url = new Url(url);
consumer.accept(this.url);
}
/**
* URL to which the request will be sent. Allows to customize additional query
* parameters if needed
* @param url url value
* @param consumer function to manipulate the URL
*/
public void url(DslProperty url, Consumer<Url> consumer) {
this.url = new Url(url);
consumer.accept(this.url);
}
/**
* URL to which the request will be sent. Allows to customize additional query.
* parameters if needed
* @param path url value
* @param consumer function to manipulate the URL
*/
public void urlPath(String path, Consumer<UrlPath> consumer) {
this.urlPath = new UrlPath(path);
consumer.accept(this.urlPath);
}
/**
* URL to which the request will be sent. Allows to customize additional query.
* parameters if needed
* @param path url value
* @param consumer function to manipulate the URL
*/
public void urlPath(DslProperty path, Consumer<UrlPath> consumer) {
this.urlPath = new UrlPath(path);
consumer.accept(this.urlPath);
}
/**
* Allows to configure HTTP headers.
* @param consumer function to manipulate the headers
*/
public void headers(Consumer<Request.RequestHeaders> consumer) {
this.headers = new Request.RequestHeaders();
consumer.accept((RequestHeaders) this.headers);
}
/**
* Allows to configure HTTP cookies.
* @param consumer function to manipulate the cookies
*/
public void cookies(Consumer<Request.RequestCookies> consumer) {
this.cookies = new Request.RequestCookies();
consumer.accept((RequestCookies) this.cookies);
}
/**
* @param consumer function to manipulate the body matchers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
@Deprecated
public void stubMatchers(Consumer<BodyMatchers> consumer) {
log.warn("stubMatchers method is deprecated. Please use bodyMatchers instead");
bodyMatchers(consumer);
}
/**
* Allows to set matchers for the body.
* @param consumer function to manipulate the URL
*/
public void bodyMatchers(Consumer<BodyMatchers> consumer) {
this.bodyMatchers = new BodyMatchers();
consumer.accept(this.bodyMatchers);
}
/**
* The URL of the contract.
* @param url url value
* @param consumer function to manipulate the URL
*/
public void url(Object url, @DelegatesTo(Url.class) Closure consumer) {
this.url = new Url(url);
consumer.setDelegate(this.url);
consumer.call();
}
/**
* URL to which the request will be sent. Allows to customize additional query
* parameters if needed
* @param url url value
* @param consumer function to manipulate the URL
*/
public void url(DslProperty url, @DelegatesTo(Url.class) Closure consumer) {
this.url = new Url(url);
consumer.setDelegate(this.url);
consumer.call();
}
/**
* URL to which the request will be sent. Allows to customize additional query.
* parameters if needed
* @param path url value
* @param consumer function to manipulate the URL
*/
public void urlPath(String path, @DelegatesTo(UrlPath.class) Closure consumer) {
this.urlPath = new UrlPath(path);
consumer.setDelegate(this.urlPath);
consumer.call();
}
/**
* URL to which the request will be sent. Allows to customize additional query.
* parameters if needed
* @param path url value
* @param consumer function to manipulate the URL
*/
public void urlPath(DslProperty path, @DelegatesTo(UrlPath.class) Closure consumer) {
this.urlPath = new UrlPath(path);
consumer.setDelegate(this.urlPath);
consumer.call();
}
/**
* Allows to configure HTTP headers.
* @param consumer function to manipulate the headers
*/
public void headers(@DelegatesTo(Request.RequestHeaders.class) Closure consumer) {
this.headers = new Request.RequestHeaders();
consumer.setDelegate(this.headers);
consumer.call();
}
/**
* Allows to configure HTTP cookies.
* @param consumer function to manipulate the cookies
*/
public void cookies(@DelegatesTo(Request.RequestCookies.class) Closure consumer) {
this.cookies = new Request.RequestCookies();
consumer.setDelegate(this.cookies);
consumer.call();
}
/**
* @param consumer function to manipulate the body matchers
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
*/
@Deprecated
public void stubMatchers(@DelegatesTo(BodyMatchers.class) Closure consumer) {
log.warn("stubMatchers method is deprecated. Please use bodyMatchers instead");
bodyMatchers(consumer);
}
/**
* Allows to set matchers for the body.
* @param consumer function to manipulate the URL
*/
public void bodyMatchers(@DelegatesTo(BodyMatchers.class) Closure consumer) {
this.bodyMatchers = new BodyMatchers();
consumer.setDelegate(this.bodyMatchers);
consumer.call();
}
static class RequestHeaders extends Headers {
private final Common common = new Common();
@Override
public DslProperty matching(String value) {
return this.common.$(this.common.c(this.common
.regex(RegexpUtils.escapeSpecialRegexWithSingleEscape(value) + ".*")),
this.common.p(value));
}
}
static class RequestCookies extends Cookies {
private final Common common = new Common();
@Override
public DslProperty matching(String value) {
return this.common.$(this.common.c(this.common
.regex(RegexpUtils.escapeSpecialRegexWithSingleEscape(value) + ".*")),
this.common.p(value));
}
}
private class ServerRequest extends Request {
ServerRequest(Request enclosing, Request request) {
super(request);
}
}
private class ClientRequest extends Request {
ClientRequest(Request enclosing, Request request) {
super(request);
}
}
private class ClientPatternValueDslProperty
extends PatternValueDslProperty<ClientDslProperty> {
@Override
protected ClientDslProperty createProperty(Pattern pattern,
Object generatedValue) {
return new ClientDslProperty(pattern, generatedValue);
}
}
}

View File

@@ -0,0 +1,864 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.contract.spec.util.RegexpUtils;
/**
* Represents the response side of the HTTP communication.
*
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @since 1.0.0
*/
public class Response extends Common implements RegexCreatingProperty<ServerDslProperty> {
private static final Log log = LogFactory.getLog(Response.class);
private DslProperty status;
private DslProperty delay;
private Headers headers;
private Cookies cookies;
private Body body;
private boolean async;
private ResponseBodyMatchers bodyMatchers;
private ServerPatternValueDslProperty property = new ServerPatternValueDslProperty();
private HttpStatus httpStatus = new HttpStatus();
public Response() {
}
public Response(Response response) {
this.status = response.getStatus();
this.headers = response.getHeaders();
this.cookies = response.getCookies();
this.body = response.getBody();
}
/**
* Allows to set the HTTP status.
* @param status HTTP status
*/
public void status(int status) {
this.status = toDslProperty(status);
}
/**
* Allows to set the HTTP status.
* @param status HTTP status
*/
public void status(DslProperty status) {
this.status = toDslProperty(status);
}
/**
* Allows set an HTTP body.
* @param body body to set
*/
public void body(Map<String, Object> body) {
this.body = new Body(convertObjectsToDslProperties(body));
}
/**
* Allows set an HTTP body.
* @param body body to set
*/
public void body(List body) {
this.body = new Body(convertObjectsToDslProperties(body));
}
/**
* Allows set an HTTP body.
* @param bodyAsValue body to set
*/
public void body(Object bodyAsValue) {
if (bodyAsValue instanceof List) {
body((List) bodyAsValue);
}
else {
this.body = new Body(bodyAsValue);
}
}
/**
* Allows to set a fixed delay of the response in milliseconds.
* @param timeInMilliseconds delay in millis
*/
public void fixedDelayMilliseconds(int timeInMilliseconds) {
this.delay = toDslProperty(timeInMilliseconds);
}
/**
* Turns on the asynchronous mode for this contract. Used with MockMvc and the Servlet
* 3.0 features.
*/
public void async() {
this.async = true;
}
@Override
public void assertThatSidesMatch(Object stubSide, Object testSide) {
if (stubSide instanceof OptionalProperty) {
throw new IllegalStateException(
"Optional can be used only in the test side of the response!");
}
super.assertThatSidesMatch(stubSide, testSide);
}
/**
* Allows to set a dynamic value for the given element.
* @param server server value
* @return dsl property
*/
public DslProperty value(ServerDslProperty server) {
Object dynamicValue = server.getServerValue();
Object concreteValue = server.getClientValue();
if (dynamicValue instanceof RegexProperty && server.isSingleValue()) {
return ((RegexProperty) dynamicValue).concreteClientDynamicProducer();
}
return new DslProperty(concreteValue, dynamicValue);
}
/**
* Allows to set a dynamic value for the given element.
* @param server server value
* @return dsl property
*/
public DslProperty $(ServerDslProperty server) {
return value(server);
}
/**
* Allows to set a dynamic value for the given element.
* @param server server value
* @return dsl property
*/
public DslProperty value(Pattern server) {
return value(new RegexProperty(server));
}
/**
* Allows to set a dynamic value for the given element.
* @param server server value
* @return dsl property
*/
public DslProperty value(RegexProperty server) {
return value(new ServerDslProperty(server));
}
/**
* Allows to set a dynamic value for the given element.
* @param server server value
* @return dsl property
*/
public DslProperty $(RegexProperty server) {
return value(server);
}
/**
* Allows to set a dynamic value for the given element.
* @param server server value
* @return dsl property
*/
public DslProperty $(Pattern server) {
return value(new RegexProperty(server));
}
@Override
public RegexProperty regexProperty(Object object) {
return new RegexProperty(object).concreteClientDynamicProducer();
}
/**
* Allows to reference entries from the request.
* @return from request object
*/
public FromRequest fromRequest() {
return new FromRequest();
}
/**
* Allows to set a dynamic value for the given element.
* @param client client value
* @param server server value
* @return dsl property
*/
@Override
public DslProperty value(ClientDslProperty client, ServerDslProperty server) {
if (client.getClientValue() instanceof RegexProperty) {
throw new IllegalStateException(
"You can't have a regular expression for the response on the client side");
}
return super.value(client, server);
}
/**
* Allows to set a dynamic value for the given element.
* @param server server value
* @param client client value
* @return dsl property
*/
@Override
public DslProperty value(ServerDslProperty server, ClientDslProperty client) {
if (client.getClientValue() instanceof RegexProperty) {
throw new IllegalStateException(
"You can't have a regular expression for the response on the client side");
}
return super.value(server, client);
}
public ServerPatternValueDslProperty getProperty() {
return property;
}
public void setProperty(ServerPatternValueDslProperty property) {
this.property = property;
}
public HttpStatus getHttpStatus() {
return httpStatus;
}
public void setHttpStatus(HttpStatus httpStatus) {
this.httpStatus = httpStatus;
}
public DslProperty getStatus() {
return status;
}
public void setStatus(DslProperty status) {
this.status = status;
}
public DslProperty getDelay() {
return delay;
}
public void setDelay(DslProperty delay) {
this.delay = delay;
}
public Headers getHeaders() {
return headers;
}
public void setHeaders(Headers headers) {
this.headers = headers;
}
public Cookies getCookies() {
return cookies;
}
public void setCookies(Cookies cookies) {
this.cookies = cookies;
}
public Body getBody() {
return body;
}
public void setBody(Body body) {
this.body = body;
}
public boolean getAsync() {
return async;
}
public boolean isAsync() {
return async;
}
public void setAsync(boolean async) {
this.async = async;
}
public ResponseBodyMatchers getBodyMatchers() {
return bodyMatchers;
}
public void setBodyMatchers(ResponseBodyMatchers bodyMatchers) {
this.bodyMatchers = bodyMatchers;
}
@Override
public ServerDslProperty anyAlphaUnicode() {
return property.anyAlphaUnicode();
}
@Override
public ServerDslProperty anyAlphaNumeric() {
return property.anyAlphaNumeric();
}
@Override
public ServerDslProperty anyNumber() {
return property.anyNumber();
}
@Override
public ServerDslProperty anyInteger() {
return property.anyInteger();
}
@Override
public ServerDslProperty anyPositiveInt() {
return property.anyPositiveInt();
}
@Override
public ServerDslProperty anyDouble() {
return property.anyDouble();
}
@Override
public ServerDslProperty anyHex() {
return property.anyHex();
}
@Override
public ServerDslProperty aBoolean() {
return property.aBoolean();
}
@Override
public ServerDslProperty anyIpAddress() {
return property.anyIpAddress();
}
@Override
public ServerDslProperty anyHostname() {
return property.anyHostname();
}
@Override
public ServerDslProperty anyEmail() {
return property.anyEmail();
}
@Override
public ServerDslProperty anyUrl() {
return property.anyUrl();
}
@Override
public ServerDslProperty anyHttpsUrl() {
return property.anyHttpsUrl();
}
@Override
public ServerDslProperty anyUuid() {
return property.anyUuid();
}
@Override
public ServerDslProperty anyDate() {
return property.anyDate();
}
@Override
public ServerDslProperty anyDateTime() {
return property.anyDateTime();
}
@Override
public ServerDslProperty anyTime() {
return property.anyTime();
}
@Override
public ServerDslProperty anyIso8601WithOffset() {
return property.anyIso8601WithOffset();
}
@Override
public ServerDslProperty anyNonBlankString() {
return property.anyNonBlankString();
}
@Override
public ServerDslProperty anyNonEmptyString() {
return property.anyNonEmptyString();
}
@Override
public ServerDslProperty anyOf(String... values) {
return property.anyOf(values);
}
public int CONTINUE() {
return httpStatus.CONTINUE();
}
public int SWITCHING_PROTOCOLS() {
return httpStatus.SWITCHING_PROTOCOLS();
}
public int PROCESSING() {
return httpStatus.PROCESSING();
}
public int CHECKPOINT() {
return httpStatus.CHECKPOINT();
}
public int OK() {
return httpStatus.OK();
}
public int CREATED() {
return httpStatus.CREATED();
}
public int ACCEPTED() {
return httpStatus.ACCEPTED();
}
public int NON_AUTHORITATIVE_INFORMATION() {
return httpStatus.NON_AUTHORITATIVE_INFORMATION();
}
public int NO_CONTENT() {
return httpStatus.NO_CONTENT();
}
public int RESET_CONTENT() {
return httpStatus.RESET_CONTENT();
}
public int PARTIAL_CONTENT() {
return httpStatus.PARTIAL_CONTENT();
}
public int MULTI_STATUS() {
return httpStatus.MULTI_STATUS();
}
public int ALREADY_REPORTED() {
return httpStatus.ALREADY_REPORTED();
}
public int IM_USED() {
return httpStatus.IM_USED();
}
public int MULTIPLE_CHOICES() {
return httpStatus.MULTIPLE_CHOICES();
}
public int MOVED_PERMANENTLY() {
return httpStatus.MOVED_PERMANENTLY();
}
public int FOUND() {
return httpStatus.FOUND();
}
@Deprecated
public int MOVED_TEMPORARILY() {
return httpStatus.MOVED_TEMPORARILY();
}
public int SEE_OTHER() {
return httpStatus.SEE_OTHER();
}
public int NOT_MODIFIED() {
return httpStatus.NOT_MODIFIED();
}
@Deprecated
public int USE_PROXY() {
return httpStatus.USE_PROXY();
}
public int TEMPORARY_REDIRECT() {
return httpStatus.TEMPORARY_REDIRECT();
}
public int PERMANENT_REDIRECT() {
return httpStatus.PERMANENT_REDIRECT();
}
public int BAD_REQUEST() {
return httpStatus.BAD_REQUEST();
}
public int UNAUTHORIZED() {
return httpStatus.UNAUTHORIZED();
}
public int PAYMENT_REQUIRED() {
return httpStatus.PAYMENT_REQUIRED();
}
public int FORBIDDEN() {
return httpStatus.FORBIDDEN();
}
public int NOT_FOUND() {
return httpStatus.NOT_FOUND();
}
public int METHOD_NOT_ALLOWED() {
return httpStatus.METHOD_NOT_ALLOWED();
}
public int NOT_ACCEPTABLE() {
return httpStatus.NOT_ACCEPTABLE();
}
public int PROXY_AUTHENTICATION_REQUIRED() {
return httpStatus.PROXY_AUTHENTICATION_REQUIRED();
}
public int REQUEST_TIMEOUT() {
return httpStatus.REQUEST_TIMEOUT();
}
public int CONFLICT() {
return httpStatus.CONFLICT();
}
public int GONE() {
return httpStatus.GONE();
}
public int LENGTH_REQUIRED() {
return httpStatus.LENGTH_REQUIRED();
}
public int PRECONDITION_FAILED() {
return httpStatus.PRECONDITION_FAILED();
}
public int PAYLOAD_TOO_LARGE() {
return httpStatus.PAYLOAD_TOO_LARGE();
}
@Deprecated
public int REQUEST_ENTITY_TOO_LARGE() {
return httpStatus.REQUEST_ENTITY_TOO_LARGE();
}
public int URI_TOO_LONG() {
return httpStatus.URI_TOO_LONG();
}
@Deprecated
public int REQUEST_URI_TOO_LONG() {
return httpStatus.REQUEST_URI_TOO_LONG();
}
public int UNSUPPORTED_MEDIA_TYPE() {
return httpStatus.UNSUPPORTED_MEDIA_TYPE();
}
public int REQUESTED_RANGE_NOT_SATISFIABLE() {
return httpStatus.REQUESTED_RANGE_NOT_SATISFIABLE();
}
public int EXPECTATION_FAILED() {
return httpStatus.EXPECTATION_FAILED();
}
public int I_AM_A_TEAPOT() {
return httpStatus.I_AM_A_TEAPOT();
}
@Deprecated
public int INSUFFICIENT_SPACE_ON_RESOURCE() {
return httpStatus.INSUFFICIENT_SPACE_ON_RESOURCE();
}
@Deprecated
public int METHOD_FAILURE() {
return httpStatus.METHOD_FAILURE();
}
@Deprecated
public int DESTINATION_LOCKED() {
return httpStatus.DESTINATION_LOCKED();
}
public int UNPROCESSABLE_ENTITY() {
return httpStatus.UNPROCESSABLE_ENTITY();
}
public int LOCKED() {
return httpStatus.LOCKED();
}
public int FAILED_DEPENDENCY() {
return httpStatus.FAILED_DEPENDENCY();
}
public int UPGRADE_REQUIRED() {
return httpStatus.UPGRADE_REQUIRED();
}
public int PRECONDITION_REQUIRED() {
return httpStatus.PRECONDITION_REQUIRED();
}
public int TOO_MANY_REQUESTS() {
return httpStatus.TOO_MANY_REQUESTS();
}
public int REQUEST_HEADER_FIELDS_TOO_LARGE() {
return httpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE();
}
public int UNAVAILABLE_FOR_LEGAL_REASONS() {
return httpStatus.UNAVAILABLE_FOR_LEGAL_REASONS();
}
public int INTERNAL_SERVER_ERROR() {
return httpStatus.INTERNAL_SERVER_ERROR();
}
public int NOT_IMPLEMENTED() {
return httpStatus.NOT_IMPLEMENTED();
}
public int BAD_GATEWAY() {
return httpStatus.BAD_GATEWAY();
}
public int SERVICE_UNAVAILABLE() {
return httpStatus.SERVICE_UNAVAILABLE();
}
public int GATEWAY_TIMEOUT() {
return httpStatus.GATEWAY_TIMEOUT();
}
public int HTTP_VERSION_NOT_SUPPORTED() {
return httpStatus.HTTP_VERSION_NOT_SUPPORTED();
}
public int VARIANT_ALSO_NEGOTIATES() {
return httpStatus.VARIANT_ALSO_NEGOTIATES();
}
public int INSUFFICIENT_STORAGE() {
return httpStatus.INSUFFICIENT_STORAGE();
}
public int LOOP_DETECTED() {
return httpStatus.LOOP_DETECTED();
}
public int BANDWIDTH_LIMIT_EXCEEDED() {
return httpStatus.BANDWIDTH_LIMIT_EXCEEDED();
}
public int NOT_EXTENDED() {
return httpStatus.NOT_EXTENDED();
}
public int NETWORK_AUTHENTICATION_REQUIRED() {
return httpStatus.NETWORK_AUTHENTICATION_REQUIRED();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Response response = (Response) o;
return async == response.async && Objects.equals(status, response.status)
&& Objects.equals(delay, response.delay)
&& Objects.equals(headers, response.headers)
&& Objects.equals(cookies, response.cookies)
&& Objects.equals(body, response.body)
&& Objects.equals(bodyMatchers, response.bodyMatchers);
}
@Override
public int hashCode() {
return Objects.hash(status, delay, headers, cookies, body, async, bodyMatchers);
}
@Override
public String toString() {
return "Response{" + "\nstatus=" + status + ", \n\tdelay=" + delay
+ ", \n\theaders=" + headers + ", \n\tcookies=" + cookies + ", \n\tbody="
+ body + ", \n\tasync=" + async + ", \n\tbodyMatchers=" + bodyMatchers
+ '}';
}
/**
* Allows to configure HTTP headers.
* @param consumer function to manipulate the URL
*/
public void headers(Consumer<ResponseHeaders> consumer) {
this.headers = new Response.ResponseHeaders();
consumer.accept((ResponseHeaders) this.headers);
}
/**
* Allows to configure HTTP cookies.
* @param consumer function to manipulate the URL
*/
public void cookies(Consumer<Response.ResponseCookies> consumer) {
this.cookies = new Response.ResponseCookies();
consumer.accept((ResponseCookies) this.cookies);
}
/**
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
* @param consumer function to manipulate the URL
*/
@Deprecated
public void testMatchers(Consumer<ResponseBodyMatchers> consumer) {
log.warn("testMatchers method is deprecated. Please use bodyMatchers instead");
bodyMatchers(consumer);
}
/**
* Allows to set matchers for the body.
* @param consumer function to manipulate the URL
*/
public void bodyMatchers(Consumer<ResponseBodyMatchers> consumer) {
this.bodyMatchers = new ResponseBodyMatchers();
consumer.accept(this.bodyMatchers);
}
/**
* Allows to configure HTTP headers.
* @param consumer function to manipulate the URL
*/
public void headers(@DelegatesTo(ResponseHeaders.class) Closure consumer) {
this.headers = new Response.ResponseHeaders();
consumer.setDelegate(this.headers);
consumer.call();
}
/**
* Allows to configure HTTP cookies.
* @param consumer function to manipulate the URL
*/
public void cookies(@DelegatesTo(Response.ResponseCookies.class) Closure consumer) {
this.cookies = new Response.ResponseCookies();
consumer.setDelegate(this.cookies);
consumer.call();
}
/**
* @deprecated Deprecated in favor of bodyMatchers to support other future
* bodyMatchers too
* @param consumer function to manipulate the URL
*/
@Deprecated
public void testMatchers(@DelegatesTo(ResponseBodyMatchers.class) Closure consumer) {
log.warn("testMatchers method is deprecated. Please use bodyMatchers instead");
bodyMatchers(consumer);
}
/**
* Allows to set matchers for the body.
* @param consumer function to manipulate the URL
*/
public void bodyMatchers(@DelegatesTo(ResponseBodyMatchers.class) Closure consumer) {
this.bodyMatchers = new ResponseBodyMatchers();
consumer.setDelegate(this.bodyMatchers);
consumer.call();
}
static class ResponseHeaders extends Headers {
private final Common common = new Common();
@Override
public DslProperty matching(final String value) {
return this.common.$(this.common.p(notEscaped(Pattern.compile(
RegexpUtils.escapeSpecialRegexWithSingleEscape(value) + ".*"))),
this.common.c(value));
}
}
static class ResponseCookies extends Cookies {
private final Common common = new Common();
@Override
public DslProperty matching(final String value) {
return this.common.$(this.common.p(this.common
.regex(RegexpUtils.escapeSpecialRegexWithSingleEscape(value) + ".*")),
this.common.c(value));
}
}
private class ServerResponse extends Response {
ServerResponse(Response enclosing, Response request) {
super(request);
}
}
private class ClientResponse extends Response {
ClientResponse(Response enclosing, Response request) {
super(request);
}
}
private class ServerPatternValueDslProperty
extends PatternValueDslProperty<ServerDslProperty> {
@Override
protected ServerDslProperty createProperty(Pattern pattern,
Object generatedValue) {
return new ServerDslProperty(pattern, generatedValue);
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Body matchers for the response side (output message, REST response).
*
* @author Marcin Grzejszczak
* @author Tim Ysewyn
* @since 1.0.3
*/
public class ResponseBodyMatchers extends BodyMatchers {
public MatchingTypeValue byType() {
MatchingTypeValue value = new MatchingTypeValue();
value.setType(MatchingType.TYPE);
return value;
}
public MatchingTypeValue byCommand(String execute) {
MatchingTypeValue value = new MatchingTypeValue();
value.setType(MatchingType.COMMAND);
value.setValue(new ExecutionProperty(execute));
return value;
}
public MatchingTypeValue byNull() {
MatchingTypeValue value = new MatchingTypeValue();
value.setType(MatchingType.NULL);
return value;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Represents a server side {@link DslProperty}.
*
* @since 1.0.0
*/
public class ServerDslProperty extends DslProperty {
public ServerDslProperty(Object singleValue) {
super(singleValue);
}
public ServerDslProperty(Object server, Object client) {
super(client, server);
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
class ServerInput extends Input {
ServerInput(Input request) {
super(request);
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
class ServerOutputMessage extends OutputMessage {
ServerOutputMessage(OutputMessage request) {
super(request);
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.Objects;
import java.util.function.Consumer;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import org.springframework.cloud.contract.spec.util.ValidateUtils;
/**
* Represents a URL that may contain query parameters.
*
* @since 1.0.0
*/
public class Url extends DslProperty {
private QueryParameters queryParameters;
public Url(DslProperty prop) {
super(prop.getClientValue(), prop.getServerValue());
ValidateUtils.validateServerValueIsAvailable(prop.getServerValue(), "Url");
}
public Url(Object url) {
super(url, testUrl(url));
ValidateUtils.validateServerValueIsAvailable(url, "Url");
}
// Can be overridable by extensions
private static Object testUrl(Object url) {
return DslPropertyConverter.INSTANCE.testSide(url);
}
public QueryParameters getQueryParameters() {
return queryParameters;
}
public void setQueryParameters(QueryParameters queryParameters) {
this.queryParameters = queryParameters;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
Url url = (Url) o;
return Objects.equals(queryParameters, url.queryParameters);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), queryParameters);
}
@Override
public String toString() {
return "Url{" + "\nqueryParameters=" + queryParameters + "} \n"
+ super.toString();
}
/**
* The query parameters part of the contract.
* @param consumer function to manipulate the query parameters
*/
public void queryParameters(Consumer<QueryParameters> consumer) {
this.queryParameters = new QueryParameters();
consumer.accept(this.queryParameters);
}
/**
* The query parameters part of the contract.
* @param consumer function to manipulate the query parameters
*/
public void queryParameters(@DelegatesTo(QueryParameters.class) Closure consumer) {
this.queryParameters = new QueryParameters();
consumer.setDelegate(this.queryParameters);
consumer.call();
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
import java.util.regex.Pattern;
/**
* Taken from https://gist.github.com/skeller88/5eb73dc0090d4ff1249a.
*/
final class UrlHelper {
/**
* Example: "http". Also called 'protocol'. Scheme component is optional, even though
* the RFC doesn't make it optional. Since ((RegexProperty) this regex is validating a
* submitted callback url, which determines where the browser will navigate to after a
* successful authentication, the browser will use http or https for the scheme by
* default. Not borrowed from dperini in order to allow any scheme type.
*/
private static final String REGEX_SCHEME = "[A-Za-z][+-.\\w^_]*:";
private static final String HTTPS_REGEX_SCHEME = "https:";
private static final String REGEX_AUTHORATIVE_DECLARATION = "/{2}";
private static final String REGEX_USERINFO = "(?:\\S+(?::\\S*)?@)?";
private static final String REGEX_HOST = "(?:"
+ "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
+ "|" + "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)"
+ "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*"
+ "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))|(?:localhost))";
private static final String REGEX_PORT = "(?::\\d{2,5})?";
private static final String REGEX_RESOURCE_PATH = "(?:/\\S*)?";
protected static final Pattern HTTPS_URL = Pattern.compile(
"^(?:" + HTTPS_REGEX_SCHEME + REGEX_AUTHORATIVE_DECLARATION + REGEX_USERINFO
+ REGEX_HOST + REGEX_PORT + REGEX_RESOURCE_PATH + ")$");
protected static final Pattern URL = Pattern.compile("^(?:(?:" + REGEX_SCHEME
+ REGEX_AUTHORATIVE_DECLARATION + ")?" + REGEX_USERINFO + REGEX_HOST
+ REGEX_PORT + REGEX_RESOURCE_PATH + ")$");
private UrlHelper() {
throw new IllegalStateException("Can't instantiate an utility class");
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.internal;
/**
* Represents a url path. Syntactic sugar when working with {@link QueryParameters}. It's
* logically equal to {@link Url}.
*
* @since 1.0.0
*/
public class UrlPath extends Url {
public UrlPath(Object path) {
super(path);
}
public UrlPath(String path) {
super(path);
}
public UrlPath(DslProperty path) {
super(path);
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.util;
import java.util.regex.Pattern;
/**
* Useful utility methods to work with regular expressions.
*
* @since 1.0.2
*/
public final class RegexpUtils {
private RegexpUtils() {
throw new IllegalStateException("Can't instantiate a utility class");
}
public static String escapeSpecialRegexChars(String str) {
return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\\\\\$0");
}
public static String escapeSpecialRegexWithSingleEscape(String str) {
return SPECIAL_REGEX_CHARS.matcher(str).replaceAll("\\\\$0");
}
private static final Pattern SPECIAL_REGEX_CHARS = Pattern
.compile("[{}()\\[\\].+*?^$\\\\|]");
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2013-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
*
* https://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 org.springframework.cloud.contract.spec.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import org.springframework.cloud.contract.spec.internal.DslProperty;
import org.springframework.cloud.contract.spec.internal.MatchingStrategy;
import org.springframework.cloud.contract.spec.internal.RegexProperty;
/**
* Checks the validity of DSL entries.
*
* @since 1.0.0
*/
public final class ValidateUtils {
private ValidateUtils() {
throw new IllegalStateException("Can't instantiate an utility class");
}
/**
* Allowed matching types on the server side.
*/
public static List<MatchingStrategy.Type> ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE = new ArrayList<>(
Arrays.asList(MatchingStrategy.Type.EQUAL_TO, MatchingStrategy.Type.ABSENT));
/**
* Validates if for given object the server value is present.
* @param value value to check
* @param msg potential exception message
* @return object if everything is fine
*/
public static Object validateServerValueIsAvailable(Object value, String msg) {
if (value instanceof Pattern) {
validateServerValue((Pattern) value, msg);
}
else if (value instanceof RegexProperty) {
RegexProperty property = (RegexProperty) value;
validateServerValue(property.getPattern(), msg);
}
else if (value instanceof MatchingStrategy) {
validateServerValue((MatchingStrategy) value, msg);
}
else if (value instanceof DslProperty) {
validateServerValue((DslProperty) value, msg);
}
else {
validateServerValue(value, msg);
}
return value;
}
public static void validateServerValue(Pattern pattern, String msg) {
throw new IllegalStateException(msg + " can\'t be a pattern for the server side");
}
public static void validateServerValue(MatchingStrategy matchingStrategy,
String msg) {
if (!ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE.contains(matchingStrategy.getType())) {
throw new IllegalStateException(msg + " can\'t be of a matching type: "
+ String.valueOf(matchingStrategy.getType())
+ " for the server side");
}
validateServerValue(matchingStrategy.getServerValue(), msg);
}
public static void validateServerValue(DslProperty value, String msg) {
validateServerValue(value.getServerValue(), msg);
}
public static void validateServerValue(Object value, String msg) {
// OK
}
}

View File

@@ -0,0 +1,146 @@
/**
* Copyright 2009 Wilfred Springer
* Copyright 2012 Jason Pell
* Copyright 2013 Antonio García-Domínguez
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
* <p>
* The class is bundled together with our code because it has not been
* released to any central repository.
*/
package repackaged.nl.flotsam.xeger;
import java.util.List;
import java.util.Random;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.State;
import dk.brics.automaton.Transition;
import org.apache.commons.lang3.StringUtils;
/**
* An object that will generate text from a regular expression. In a way, it's the
* opposite of a regular expression matcher: an instance of this class will produce text
* that is guaranteed to match the regular expression passed in.
*
* slight modifications by Marcin Grzejszczak
*/
public class Xeger {
// Added by Marcin Grzejszczak
// may lead to stackoverflow when regex is unbounded
static int ITERATION_LIMIT = 200;
private final Automaton automaton;
private Random random;
/**
* Constructs a new instance, accepting the regular expression and the randomizer.
* @param regex The regular expression. (Not <code>null</code>.)
* @param random The object that will randomize the way the String is generated. (Not
* <code>null</code>.)
* @throws IllegalArgumentException If the regular expression is invalid.
*/
public Xeger(String regex, Random random) {
assert regex != null;
assert random != null;
// https://stackoverflow.com/questions/1578789/how-do-i-generate-text-matching-a-regular-expression-from-a-regular-expression
String pattern = regex.replace("\\d", "[0-9]") // Used d=Digit
.replace("\\w", "[A-Za-z0-9_]") // Used =Word
.replace("\\s", "[ \t\r\n]"); // Used s="White"Space
this.automaton = new RegExp(pattern).toAutomaton();
this.random = random;
String generatedCharsSysProp = System
.getProperty("springCloudContractGeneratedCharsFromRegex");
String generatedCharsEnvVar = System
.getenv("SPRING_CLOUD_CONTRACT_GENERATED_CHARS_FROM_REGEX");
if (StringUtils.isNotEmpty(generatedCharsSysProp)) {
ITERATION_LIMIT = Integer.parseInt(generatedCharsSysProp);
}
else if (StringUtils.isNotEmpty(generatedCharsEnvVar)) {
ITERATION_LIMIT = Integer.parseInt(generatedCharsEnvVar);
}
}
/**
* As {@link Xeger#Xeger(String, java.util.Random)}, creating a
* {@link java.util.Random} instance implicityly.
* @param regex as string
*/
public Xeger(String regex) {
this(regex, new Random());
}
/**
* Generates a random number within the given bounds.
* @param min The minimum number (inclusive).
* @param max The maximum number (inclusive).
* @param random The object used as the randomizer.
* @return A random number in the given range.
*/
static int getRandomInt(int min, int max, Random random) {
// Use random.nextInt as it guarantees a uniform distribution
int maxForRandom = max - min + 1;
return random.nextInt(maxForRandom) + min;
}
/**
* Generates a random String that is guaranteed to match the regular expression passed
* to the constructor.
* @return generated regexp
*/
public String generate() {
StringBuilder builder = new StringBuilder();
int counter = 0;
generate(builder, this.automaton.getInitialState(), counter);
return builder.toString();
}
private void generate(StringBuilder builder, State state, int counter) {
if (counter >= ITERATION_LIMIT) {
return;
}
List<Transition> transitions = state.getSortedTransitions(false);
if (transitions.size() == 0) {
assert state.isAccept();
return;
}
int nroptions = state.isAccept() ? transitions.size() : transitions.size() - 1;
int option = Xeger.getRandomInt(0, nroptions, this.random);
if (state.isAccept() && option == 0) { // 0 is considered stop
return;
}
// Moving on to next transition
Transition transition = transitions.get(option - (state.isAccept() ? 1 : 0));
appendChoice(builder, transition);
generate(builder, transition.getDest(), ++counter);
}
private void appendChoice(StringBuilder builder, Transition transition) {
char c = (char) Xeger.getRandomInt(transition.getMin(), transition.getMax(),
this.random);
builder.append(c);
}
public Random getRandom() {
return this.random;
}
public void setRandom(Random random) {
this.random = random;
}
}