YAML contracts (#522)

with this change we allow the users to use YAML to define contracts

fixes gh-300 gh-508
helps with #501
This commit is contained in:
Marcin Grzejszczak
2018-01-19 00:27:15 +01:00
committed by GitHub
parent b2428c5f13
commit b745d9a7e3
125 changed files with 5116 additions and 272 deletions

View File

@@ -130,6 +130,7 @@ Assume that you want to send a request containing the ID of a client company and
amount it wants to borrow from us. You also want to send it to the /fraudcheck url via
the PUT method.
.Groovy DSL
[source,groovy,indent=0]
----
package contracts
@@ -191,6 +192,63 @@ From the Producer perspective, in the autogenerated producer-side test:
*/
----
.YAML
[source,yml,indent=0]
----
request: # (1)
method: PUT # (2)
url: /fraudcheck # (3)
body: # (4)
"client.id": 1234567890
loanAmount: 99999
headers: # (5)
Content-Type: application/json
matchers:
body:
- path: $.['client.id'] # (6)
type: by_regex
value: "[0-9]{10}"
response: # (7)
status: 200 # (8)
body: # (9)
fraudCheckStatus: "FRAUD"
"rejection.reason": "Amount too high"
headers: # (10)
Content-Type: application/json;charset=UTF-8
#From the Consumer perspective, when shooting a request in the integration test:
#
#(1) - If the consumer sends a request
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `clientId`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
#(7) - then the response will be sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json`
#
#From the Producer perspective, in the autogenerated producer-side test:
#
#(1) - A request will be sent to the producer
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `clientId` `1234567890`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(7) - then the test will assert if the response has been sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`
----
==== Client Side
Spring Cloud Contract generates stubs, which you can use during client-side testing.
@@ -231,7 +289,7 @@ your application behaves in a different way, especially in production.
To ensure that your application behaves the way you define in your stub, tests are
generated from the stub you provide.
The autogenerated test looks like this:
The autogenerated test looks, more or less, like this:
[source,java,indent=0]
----
@@ -418,7 +476,7 @@ clone it.
[source,bash,indent=0]
----
git clone https://your-git-server.com/server-side.git local-http-server-repo
$ git clone https://your-git-server.com/server-side.git local-http-server-repo
----
*Define the contract locally in the repo of Fraud Detection service.*
@@ -429,6 +487,7 @@ your expectations. To do so, write the following contract:
IMPORTANT: Place the contract under `src/test/resources/contracts/fraud` folder. The `fraud` folder
is important because the producer's test base class name references that folder.
.Groovy DSL
[source,groovy,indent=0]
----
package contracts
@@ -490,8 +549,66 @@ From the Producer perspective, in the autogenerated producer-side test:
*/
----
The Contract is written using a statically typed Groovy DSL. You might wonder what about
those `value(client(...), server(...))` parts. By using this notation, Spring Cloud
.YAML
[source,yml,indent=0]
----
request: # (1)
method: PUT # (2)
url: /fraudcheck # (3)
body: # (4)
"client.id": 1234567890
loanAmount: 99999
headers: # (5)
Content-Type: application/json
matchers:
body:
- path: $.['client.id'] # (6)
type: by_regex
value: "[0-9]{10}"
response: # (7)
status: 200 # (8)
body: # (9)
fraudCheckStatus: "FRAUD"
"rejection.reason": "Amount too high"
headers: # (10)
Content-Type: application/json;charset=UTF-8
#From the Consumer perspective, when shooting a request in the integration test:
#
#(1) - If the consumer sends a request
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `clientId`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
#(7) - then the response will be sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json`
#
#From the Producer perspective, in the autogenerated producer-side test:
#
#(1) - A request will be sent to the producer
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `clientId` `1234567890`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(7) - then the test will assert if the response has been sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`
----
The YML contract is quite straight-forward. However when you take a look at the Contract
written using a statically typed Groovy DSL - you might wonder what the
`value(client(...), server(...))` parts are. By using this notation, Spring Cloud
Contract lets you define parts of a JSON block, a URL, etc., which are dynamic. In case
of an identifier or a timestamp, you need not hardcode a value. You want to allow some
different ranges of values. To enable ranges of values, you can set regular expressions
@@ -565,8 +682,8 @@ stubs. You need to skip the test generation and execution. When you execute:
[source,bash,indent=0]
----
cd local-http-server-repo
./mvnw clean install -DskipTests
$ cd local-http-server-repo
$ ./mvnw clean install -DskipTests
----
In the logs, you see something like this:
@@ -691,8 +808,8 @@ return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
[source,bash,indent=0]
----
git checkout -b contract-change-pr master
git pull https://your-git-server.com/server-side-fork.git contract-change-pr
$ git checkout -b contract-change-pr master
$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr
----
You must add the dependencies needed by the autogenerated tests:
@@ -802,8 +919,9 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
}
----
As you can see, all the `producer()` parts of the Contract that were present in the
If you used the Groovy DSL, you can see, all the `producer()` parts of the Contract that were present in the
`value(consumer(...), producer(...))` blocks got injected into the test.
In case of using YAML, the same applied for the `matchers` sections of the `response`.
Note that, on the producer side, you are also doing TDD. The expectations are expressed
in the form of a test. This test sends a request to our own application with the URL,
@@ -837,9 +955,9 @@ Once you finish your work, you can deploy your change. First, merge the branch:
[source,bash,indent=0]
----
git checkout master
git merge --no-ff contract-change-pr
git push origin master
$ git checkout master
$ git merge --no-ff contract-change-pr
$ git push origin master
----
Your CI might run something like `./mvnw clean deploy`, which would publish both the
@@ -853,8 +971,8 @@ As a developer of the Loan Issuance service (a consumer of the Fraud Detection s
[source,bash,indent=0]
----
git checkout master
git merge --no-ff contract-change-pr
$ git checkout master
$ git merge --no-ff contract-change-pr
----
*Work online.*
@@ -1149,6 +1267,23 @@ Spring Boot embedded servers, and Wiremock itself has "native" support for a par
version of Jetty (currently 9.2). To use the native Jetty, you need to add the native
Wiremock dependencies and exclude the Spring Boot container (if there is one).
=== Customization of WireMock configuration
You can register a bean of `org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer` type
in order to customize the WireMock configuration (e.g. add custom transformers).
Example:
[source,java,indent=0]
----
@Bean WireMockConfigurationCustomizer optionsCustomizer() {
return new WireMockConfigurationCustomizer() {
@Override public void customize(WireMockConfiguration options) {
// perform your customization here
}
};
}
----
=== Generating Stubs using REST Docs
https://projects.spring.io/spring-restdocs[Spring REST Docs] can be used to generate
@@ -1300,12 +1435,13 @@ Consider the following test:
.accept(MediaType.APPLICATION_PDF)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content("{\"foo\": 23 }"))
.content("{\"foo\": 23, \"bar\" : \"baz\" }"))
.andExpect(status().isOk())
.andExpect(content().string("bar"))
// first WireMock
.andDo(WireMockRestDocs.verify()
.jsonPath("$[?(@.foo >= 20)]")
.jsonPath("$[?(@.bar in ['baz','bazz','bazzz'])]")
.contentType(MediaType.valueOf("application/json"))
.stub("shouldGrantABeerIfOldEnough"))
// then Contract DSL documentation

View File

@@ -1,33 +1,36 @@
== Contract DSL
IMPORTANT: Remember that, inside the contract file, you have to provide the fully
Spring Cloud Contract supports out of the box 2 types of DSL. One written in
`Groovy` and one written in `YAML`.
If you decide to write the contract in Groovy, do not be alarmed if you have not used Groovy
before. Knowledge of the language is not really needed, as the Contract DSL uses only a
tiny subset of it (only literals, method calls and closures). Also, the DSL is statically
typed, to make it programmer-readable without any knowledge of the DSL itself.
IMPORTANT: Remember that, inside the Groovy contract file, you have to provide the fully
qualified name to the `Contract` class and `make` static imports, such as
`org.springframework.cloud.spec.Contract.make { ... }`. You can also provide an import to
the `Contract` class: `import org.springframework.cloud.spec.Contract` and then call
`Contract.make { ... }`.
Contract DSL is written in Groovy, but do not be alarmed if you have not used Groovy
before. Knowledge of the language is not really needed, as the Contract DSL uses only a
tiny subset of it (only literals, method calls and closures). Also, the DSL is statically
typed, to make it programmer-readable without any knowledge of the DSL itself.
TIP: Spring Cloud Contract supports defining multiple contracts in a single file.
The Contract is present in the `spring-cloud-contract-spec` module of the
https://github.com/spring-cloud/spring-cloud-contract/tree/master/spring-cloud-contract-verifier[Spring
Cloud Contract Verifier repository].
The following is a complete example of a contract definition:
The following is a complete example of a Groovy contract definition:
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=dsl_example,indent=0]
----
NOTE: The preceding example does not contain all the features of the DSL appear. The
remainder of this section describes the other features.
The following is a complete example of a YAML contract definition:
You can compile Contracts to WireMock stubs mapping using standalone maven command:
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_rest.yml[indent=0]
----
TIP: You can compile contracts to stubs mapping using standalone maven command:
`mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert`
=== Limitations
@@ -41,7 +44,7 @@ to turn it on, please set the value of the following system property to `true`:
You can also provide the `assertJsonSize` property in the plugin configuration.
WARNING: Because JSON structure can have any form, it can be impossible to parse it
properly when using the `value(consumer(...), producer(...))` notation in `GString`. That
properly when using the Groovy DSL and the `value(consumer(...), producer(...))` notation in `GString`. That
is why you should use the Groovy Map notation.
=== Common Top-Level elements
@@ -60,11 +63,18 @@ The following sections describe the most common top-level elements:
You can add a `description` to your contract. The description is arbitrary text. The
following code shows an example:
.Groovy DSL
[source,groovy,indent=0]
----
include::{contract_spec_path}/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy[tags=description,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_rest.yml[indent=0]
----
[[contract-dsl-name]]
==== Name
@@ -78,17 +88,36 @@ generated test not compile. Also, remember that, if you provide the same name fo
multiple contracts, your autogenerated tests fail to compile and your generated stubs
override each other.
.Groovy DSL
[source,groovy,indent=0]
----
include::{contract_spec_path}/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy[tags=name,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=name,indent=0]
----
[[contract-dsl-ignoring-contracts]]
==== Ignoring Contracts
If you want to ignore a contract, you can either set a value of ignored contracts in the
plugin configuration or set the `ignored` property on the contract itself:
.Groovy DSL
[source,groovy,indent=0]
----
include::{contract_spec_path}/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy[tags=ignored,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=ignored,indent=0]
----
[[contract-dsl-passing-values-from-files]]
==== Passing Values from Files
@@ -108,11 +137,18 @@ following resources in our project.
Further assume that your contract is as follows:
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/resources/classpath/readFromFile.groovy[indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_from_file.yml[indent=0]
----
Further assume that the JSON files is as follows:
*request.json*
@@ -128,9 +164,8 @@ include::{verifier_core_path}/src/test/resources/classpath/response.json[indent=
----
When test or stub generation takes place, the contents of the file is passed to the body
of a request or a response. That works because of the `file(...)` method. The argument of
that method needs to be a file with location relative to the folder in which the contract
lays.
of a request or a response. The name of the file needs to be a file with location
relative to the folder in which the contract lays.
[[contract-dsl-http-top-level-elements]]
==== HTTP Top-Level Elements
@@ -138,62 +173,124 @@ lays.
The following methods can be called in the top-level closure of a contract definition.
`request` and `response` are mandatory. `priority` is optional.
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=http_dsl,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=priority,indent=0]
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0]
...
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response,indent=0]
...
----
IMPORTANT: If you want to make your contract have a **higher** value of priority
you need to pass a **lower** number to the `priority` tag / method. E.g. `priority` with
value `5` has **higher** priority than `priority` with value `10`.
=== Request
The HTTP protocol requires only **method and address** to be specified in a request. The
The HTTP protocol requires only **method and url** to be specified in a request. The
same information is mandatory in request definition of the Contract.
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=request,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request_obligatory,indent=0]
----
It is possible to specify an absolute rather than relative `url`, but using `urlPath` is
the recommended way, as doing so makes the tests **host-independent**.
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=url,indent=0]
----
`request` may contain **query parameters**, which are specified in a closure nested in a
call to `urlPath` or `url`.
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_rest_with_path.yml[tags=url_path,indent=0]
----
`request` may contain **query parameters**.
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=urlpath,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0]
...
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=query_params,indent=0]
----
`request` may contain additional **request headers**, as shown in the following example:
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=headers,indent=0]
----
`request` may contain a **request body**, as shown in the following example:
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0]
...
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=headers,indent=0]
----
`request` may contain a **request body**:
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=body,indent=0]
----
`request` may contain **multipart** elements. To include multipart elements, call the
`multipart()` method, as shown in the following example
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0]
...
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=body,indent=0]
----
`request` may contain **multipart** elements. To include multipart elements, use the
`multipart` method/section, as shown in the following examples
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=multipartdsl,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_multipart.yml[indent=0]
----
In the preceding example, we define parameters in either of two ways:
.Groovy DSL
* Directly, by using the map notation, where the value can be a dynamic property (such as
`formParameter: $(consumer(...), producer(...))`).
* By using the `named(...)` method that lets you set a named parameter. A named parameter
@@ -201,6 +298,20 @@ can set a `name` and `content`. You can call it either via a method with two ar
such as `named("fileName", "fileContent")`, or via a map notation, such as
`named(name: "fileName", content: "fileContent")`.
.YAML
* The multipart parameters are set via `multipart.params` section
* The named parameters (the `fileName` and `fileContent` for a given parameter name)
can be set via the `multipart.named` section. That section contains
the `paramName` (name of the parameter), `fileName` (name of the file),
`fileContent` (content of the file) fields
* The dynamic bits can be set via the `matchers.multipart` section
** for parameters use the `params` section that can accept
`regex` or a `predefined` regular expression
** for named params use the `named` section where first you
define the parameter name via `paramName` and then you can pass the
parametrization of either `fileName` or `fileContent` via
`regex` or a `predefined` regular expression
From this contract, the generated test is as follows:
[source,java,indent=0]
@@ -227,17 +338,26 @@ The WireMock stub is as follows:
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/WireMockGroovyDslSpec.groovy[tags=multipartwiremock,indent=0]
----
=== Response
The response must contain an **HTTP status code** and may contain other information. The
following code shows an example:
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=response,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response,indent=0]
...
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response_obligatory,indent=0]
----
Besides status, the response may contain **headers** and a **body**, both of which are
specified the same way as in the request (see the previous paragraph).
@@ -245,12 +365,19 @@ specified the same way as in the request (see the previous paragraph).
The contract can contain some dynamic properties: timestamps, IDs, and so on. You do not
want to force the consumers to stub their clocks to always return the same value of time
so that it gets matched by the stub. You can provide the dynamic parts in your contracts
so that it gets matched by the stub.
For Groovy DSL you can provide the dynamic parts in your contracts
in two ways: pass them directly in the body or set them in separate sections called
`testMatchers` and `stubMatchers`.
For YAML you can only use the `matchers` section.
==== Dynamic properties inside the body
IMPORTANT: This section is valid only for Groovy DSL. Check out the
<<contract-matchers>> section for YAML examples of a similar feature.
You can set the properties inside the body either with the `value` method or, if you use
the Groovy map notation, with `$()`. The following example shows how to set dynamic
properties with the value method:
@@ -278,6 +405,9 @@ method. Subsequent sections take a closer look at what you can do with those val
==== Regular expressions
IMPORTANT: This section is valid only for Groovy DSL. Check out the
<<contract-matchers>> section for YAML examples of a similar feature.
You can use regular expressions to write your requests in Contract DSL. Doing so is
particularly useful when you want to indicate that a given response should be provided
for requests that follow a given pattern. Also, you can use regular expressions when you
@@ -319,6 +449,9 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract
==== Passing Optional Parameters
IMPORTANT: This section is valid only for Groovy DSL. Check out the
<<contract-matchers>> section for YAML examples of a similar feature.
It is possible to provide optional parameters in your contract. However, you can provide
optional parameters only for the following:
@@ -351,6 +484,9 @@ include::{plugins_path}/spring-cloud-contract-converters/src/test/groovy/org/spr
==== Executing Custom Methods on the Server Side
IMPORTANT: This section is valid only for Groovy DSL. Check out the
<<contract-matchers>> section for YAML examples of a similar feature.
You can define a method call that executes on the server side during the test. Such a
method can be added to the class defined as "baseClassForTests" in the configuration. The
following code shows an example of the contract portion of the test case:
@@ -414,7 +550,9 @@ It should resemble the following code:
==== Referencing the Request from the Response
The best situation is to provide fixed values, but sometimes you need to reference a
request in your response. To do so, you can use the `fromRequest()` method, which lets
request in your response.
If you're writing contracts using Groovy DSL, you can use the `fromRequest()` method, which lets
you reference a bunch of elements from the HTTP request. You can use the following
options:
@@ -430,13 +568,36 @@ given name.
* `fromRequest().body(String jsonPath)`: Returns the element from the request that
matches the JSON Path.
If you're using the YAML contract definition you have to use the
http://handlebarsjs.com/[Handlebars] `{{{ }}}` notation with custom, Spring Cloud Contract
functions to achieve this.
* `{{{ request.url }}}`: Returns the request URL and query parameters.
* `{{{ request.query.key.[index] }}}`: Returns the nth query parameter with a given name.
E.g. for key `foo`, first entry `{{{ request.query.foo.[0] }}}`
* `{{{ request.path }}}`: Returns the full path.
* `{{{ request.path.[index] }}}`: Returns the nth path element. E.g.
for first entry ```{{{ request.path.[0] }}}
* `{{{ request.headers.key }}}`: Returns the first header with a given name.
* `{{{ request.headers.key.[index] }}}`: Returns the nth header with a given name.
* `{{{ request.body }}}`: Returns the full request body.
* `{{{ jsonpath this 'your.json.path' }}}`: Returns the element from the request that
matches the JSON Path. E.g. for json path `$.foo` - `{{{ jsonpath this '$.foo' }}}`
Consider the following contract:
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=template_contract,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_reference_request.yml[indent=0]
----
Running a JUnit test generation leads to a test that resembles the following example:
[source,java,indent=0]
@@ -561,6 +722,7 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract
IMPORTANT: Remember to override the `applyGlobally()` method and set it to `false` if you
want the transformation to be applied only for a mapping that explicitly requires it.
[[contract-matchers]]
==== Dynamic Properties in the Matchers Sections
If you work with https://docs.pact.io/[Pact], the following discussion may seem familiar.
@@ -577,6 +739,8 @@ contract.
Currently, Spring Cloud Contract Verifier supports only JSON Path-based matchers with the
following matching possibilities:
.Groovy DSL
* For `stubMatchers`:
** `byEquality()`: The value taken from the response via the provided JSON Path must be
equal to the value provided in the contract.
@@ -615,13 +779,63 @@ following, depending on the JSON path:
*** `Number`: If you point to `Integer`, `Double`, or other kind of number.
*** `Boolean`: If you point to a `Boolean`.
.YAML
_Please read the Groovy section for detailed explanation of
what the types mean_
For YAML the structure of a matcher looks like this
[source,yml,indent=0]
----
- path: $.foo
type: by_regex
value: bar
----
Or if you want to use one of the predefined regular expressions
`[only_alpha_unicode, number, any_boolean, ip_address, hostname,
email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]`:
[source,yml,indent=0]
----
- path: $.foo
type: by_regex
predefined: only_alpha_unicode
----
Below you can find the allowed list of `type`s.
* For `stubMatchers`:
** `by_equality`
** `by_regex`
** `by_date`
** `by_timestamp`
** `by_time`
* For `testMatchers`:
** `by_equality`
** `by_regex`
** `by_date`
** `by_timestamp`
** `by_time`
** `by_type`
*** there are 2 additional fields accepted: `minOccurrence` and `maxOccurrence`.
** `by_command`
Consider the following example:
.Groovy DSL
[source,groovy,indent=0]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy[tags=matchers,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_matchers.yml[indent=0]
----
In the preceding example, you can see the dynamic portions of the contract in the
`matchers` sections. For the request part, you can see that, for all fields but
`valueWithoutAMatcher`, the values of the regular expressions that the stub should
@@ -784,6 +998,7 @@ If you're using asynchronous communication on the server side (your controllers
returning `Callable`, `DeferredResult`, and so on), then, inside your contract, you must
provide a `sync()` method in the `response` section. The following code shows an example:
.Groovy DSL
[source,groovy,indent=0]
----
org.springframework.cloud.contract.spec.Contract.make {
@@ -799,6 +1014,13 @@ org.springframework.cloud.contract.spec.Contract.make {
}
----
.YAML
[source,yml,indent=0]
----
response:
async: true
----
=== Working with Context Paths
Spring Cloud Contract supports context paths.
@@ -871,11 +1093,18 @@ following sections explain the differences:
The output message can be triggered by calling a method (such as a `Scheduler` when a was
started and a message was sent), as shown in the following example:
.Groovy DSL
[source,groovy]
----
include::{tests_path}/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy[tags=method_trigger,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_method.yml[indent=0]
----
In the previous example case, the output message is sent to `output` if a method called
`bookReturnedTriggered` is executed. On the message *publisher's* side, we generate a
test that calls that method to trigger the message. On the *consumer* side, you can use
@@ -887,11 +1116,18 @@ the `some_label` to trigger the message.
The output message can be triggered by receiving a message, as shown in the following
example:
.Groovy DSL
[source,groovy]
----
include::{tests_path}/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy[tags=message_trigger,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_input_message.yml[indent=0]
----
In the preceding example, the output message is sent to `output` if a proper message is
received on the `input` destination. On the message *publisher's* side, the engine
generates a test that sends the input message to the defined destination. On the
@@ -901,6 +1137,8 @@ generates a test that sends the input message to the defined destination. On the
[[contract-dsl-consumer-producer]]
==== Consumer/Producer
IMPORTANT: This section is valid only for Groovy DSL.
In HTTP, you have a notion of `client`/`stub and `server`/`test` notation. You can also
use those paradigms in messaging. In addition, Spring Cloud Contract Verifier also
provides the `consumer` and `producer` methods, as presented in the following example
@@ -915,21 +1153,28 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract
[[contract-dsl-common]]
==== Common
In the `input {}` or `outputMessage {}` section you can call `assertThat` with the name
In the `input` or `outputMessage` section you can call `assertThat` with the name
of a `method` (e.g. `assertThatMessageIsOnTheQueue()`) that you have defined in the
base class or in a static import. Spring Cloud Pipelines will execute that method
in the genertaed test.
base class or in a static import. Spring Cloud Contract will execute that method
in the generated test.
=== Multiple Contracts in One File
You can define multiple contracts in one file. Such a contract might resemble the
following example:
.Groovy DSL
[source,groovy,indent=0]
----
include::{plugins_path}/spring-cloud-contract-maven-plugin/src/test/projects/multiple-contracts/src/test/resources/contracts/com/hello/v1/WithList.groovy[lines=18..-1,indent=0]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/multiple_contracts.yml[indent=0]
----
In the preceding example, one contract has the `name` field and the other does not. This
leads to generation of two tests that look more or less like this:
@@ -1001,6 +1246,8 @@ your tests far more meaningful.
== Customization
IMPORTANT: This section is valid only for Groovy DSL
You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in
the remainder of this section.
@@ -1102,13 +1349,6 @@ can generate stubs for other HTTP server implementations).
=== Custom Contract Converter
Assume that your contract is written in a YAML file as follows:
[source,yml]
----
include::{verifier_core_path}/src/test/resources/contract.yml[indent=0]
----
The `ContractConverter` interface lets you register your own implementation of a contract
structure converter. The following code listing shows the `ContractConverter` interface:
@@ -1128,15 +1368,8 @@ The following example shows a typical `spring.factories` file:
[source]
----
include::{verifier_core_path}/src/test/resources/META-INF/spring.factories[indent=0]
----
The following example shows a typical YAML implementation that matches the preceding
example:
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy[indent=0,lines=16..-1]
org.springframework.cloud.contract.spec.ContractConverter=\
org.springframework.cloud.contract.verifier.converter.YamlContractConverter
----
==== Pact Converter

View File

@@ -15,6 +15,10 @@ Spring Cloud Contract Verifier stand out on the "market" of Consumer Driven Cont
- Stub Runner functionality - the stubs are automatically downloaded at runtime from Nexus / Artifactory
- Spring Cloud integration - no discovery service is needed for integration tests
=== I don't want to write a contract in Groovy!
No problem. You can write a contract in YAML!
=== What is this value(consumer(), producer()) ?
One of the biggest challenges related to stubs is their reusability. Only if they can be vastly used, will they serve their purpose.
@@ -370,4 +374,5 @@ for this for WireMock. In case of other HTTP server stubs you'll have to impleme
==== Can I reference text from file?
Yes! With version 1.2.0 we've added such a possibility. It's enough to call `file(...)` method in the
DSL and provide a path relative to where the contract lays.
DSL and provide a path relative to where the contract lays.
If you're using YAML just use the `bodyFromFile` property.

View File

@@ -106,11 +106,18 @@ Assume that you want to send a request containing the ID of a client company and
amount it wants to borrow from us. You also want to send it to the /fraudcheck url via
the PUT method.
.Groovy DSL
[source,groovy,indent=0]
----
include::{introduction_url}/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy[]
----
.YAML
[source,yml,indent=0]
----
include::{introduction_url}/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml[]
----
==== Client Side
Spring Cloud Contract generates stubs, which you can use during client-side testing.
@@ -144,7 +151,7 @@ your application behaves in a different way, especially in production.
To ensure that your application behaves the way you define in your stub, tests are
generated from the stub you provide.
The autogenerated test looks like this:
The autogenerated test looks, more or less, like this:
[source,java,indent=0]
----
@@ -260,7 +267,7 @@ clone it.
[source,bash,indent=0]
----
git clone https://your-git-server.com/server-side.git local-http-server-repo
$ git clone https://your-git-server.com/server-side.git local-http-server-repo
----
*Define the contract locally in the repo of Fraud Detection service.*
@@ -271,13 +278,21 @@ your expectations. To do so, write the following contract:
IMPORTANT: Place the contract under `src/test/resources/contracts/fraud` folder. The `fraud` folder
is important because the producer's test base class name references that folder.
.Groovy DSL
[source,groovy,indent=0]
----
include::{introduction_url}/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy[]
----
The Contract is written using a statically typed Groovy DSL. You might wonder what about
those `value(client(...), server(...))` parts. By using this notation, Spring Cloud
.YAML
[source,yml,indent=0]
----
include::{introduction_url}/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml[]
----
The YML contract is quite straight-forward. However when you take a look at the Contract
written using a statically typed Groovy DSL - you might wonder what the
`value(client(...), server(...))` parts are. By using this notation, Spring Cloud
Contract lets you define parts of a JSON block, a URL, etc., which are dynamic. In case
of an identifier or a timestamp, you need not hardcode a value. You want to allow some
different ranges of values. To enable ranges of values, you can set regular expressions
@@ -333,8 +348,8 @@ stubs. You need to skip the test generation and execution. When you execute:
[source,bash,indent=0]
----
cd local-http-server-repo
./mvnw clean install -DskipTests
$ cd local-http-server-repo
$ ./mvnw clean install -DskipTests
----
In the logs, you see something like this:
@@ -440,8 +455,8 @@ include::{introduction_url}/samples/standalone/dsl/http-server/src/main/java/com
[source,bash,indent=0]
----
git checkout -b contract-change-pr master
git pull https://your-git-server.com/server-side-fork.git contract-change-pr
$ git checkout -b contract-change-pr master
$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr
----
You must add the dependencies needed by the autogenerated tests:
@@ -511,8 +526,9 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
}
----
As you can see, all the `producer()` parts of the Contract that were present in the
If you used the Groovy DSL, you can see, all the `producer()` parts of the Contract that were present in the
`value(consumer(...), producer(...))` blocks got injected into the test.
In case of using YAML, the same applied for the `matchers` sections of the `response`.
Note that, on the producer side, you are also doing TDD. The expectations are expressed
in the form of a test. This test sends a request to our own application with the URL,
@@ -543,9 +559,9 @@ Once you finish your work, you can deploy your change. First, merge the branch:
[source,bash,indent=0]
----
git checkout master
git merge --no-ff contract-change-pr
git push origin master
$ git checkout master
$ git merge --no-ff contract-change-pr
$ git push origin master
----
Your CI might run something like `./mvnw clean deploy`, which would publish both the
@@ -559,8 +575,8 @@ As a developer of the Loan Issuance service (a consumer of the Fraud Detection s
[source,bash,indent=0]
----
git checkout master
git merge --no-ff contract-change-pr
$ git checkout master
$ git merge --no-ff contract-change-pr
----
*Work online.*

View File

@@ -89,11 +89,18 @@ it is resolved as a channel name. For *Camel*, that's a certain component (for e
Here is an example for Camel. For the given contract:
.Groovy DSL
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_method_dsl]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario1.yml[indent=0]
----
The following JUnit test is created:
[source,groovy]
@@ -112,11 +119,18 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract
Here is an example for Camel. For the given contract:
.Groovy DSL
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_dsl]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario2.yml[indent=0]
----
The following JUnit test is created:
[source,groovy]
@@ -135,11 +149,18 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract
Here is an example for Camel. For the given contract:
.Groovy DSL
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_dsl]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario3.yml[indent=0]
----
The following JUnit test is created:
[source,groovy]

View File

@@ -875,7 +875,8 @@ exclude the unwanted dependencies.
You can handle scenarios with Spring Cloud Contract Verifier. All you need to do is to
stick to the proper naming convention while creating your contracts. The convention
requires including an order number followed by an underscore, as shown in this example:
requires including an order number followed by an underscore. This will work regardles
of whether you're working with YAML or Groovy. Example:
[source,indent=0]
----

View File

@@ -7,7 +7,7 @@ buildscript {
maven { url "http://repo.spring.io/release" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE"
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
}
}

View File

@@ -9,7 +9,7 @@ buildscript {
}
// end::repos[]
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE"
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}"
}
}

View File

@@ -1,9 +1,9 @@
package contracts.fraudname
org.springframework.cloud.contract.spec.Contract.make {
// highest priority
priority(1)
request {
// highest priority
priority(1)
method PUT()
url '/frauds/name'
body([

View File

@@ -7,7 +7,7 @@ buildscript {
maven { url "http://repo.spring.io/release" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE"
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
}
}

View File

@@ -7,7 +7,7 @@ buildscript {
maven { url "http://repo.spring.io/release" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE"
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}"
classpath "com.jayway.restassured:rest-assured:2.9.0"
classpath "com.jayway.restassured:spring-mock-mvc:2.9.0"

View File

@@ -1,11 +0,0 @@
package contracts
org.springframework.cloud.contract.spec.Contract.make {
request {
method(GET())
url("/foo")
}
response {
status(200)
}
}

View File

@@ -0,0 +1,6 @@
description: Should return 200 for /foo
request:
url: /foo
method: GET
response:
status: 200

View File

@@ -7,7 +7,7 @@ buildscript {
maven { url "http://repo.spring.io/release" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE"
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
}
}

View File

@@ -7,7 +7,7 @@ buildscript {
maven { url "http://repo.spring.io/release" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE"
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}"
//tag::pact_dependency[]
classpath "org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}"

View File

@@ -26,6 +26,7 @@
<module>dsl</module>
<module>messaging</module>
<module>pact</module>
<module>yml</module>
</modules>
<build>

View File

@@ -7,7 +7,7 @@ buildscript {
maven { url "http://repo.spring.io/release" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE"
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
}
}

View File

@@ -10,7 +10,7 @@ buildscript {
maven { url "http://repo.spring.io/plugins-staging-local/" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE"
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
}
}

View File

@@ -0,0 +1,6 @@
target/
.gradle
build/

View File

@@ -0,0 +1 @@
-Xmx1024m -XX:MaxPermSize=256m -Djava.awt.headless=true

View File

@@ -0,0 +1 @@
-T2

Binary file not shown.

View File

@@ -0,0 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip

View File

@@ -0,0 +1,26 @@
= Http Client
== Prerequisites
First you have to publish to Maven Local the stubs of the *http-server* module
== How to run it?
Run
[source=groovy]
--------
./gradlew clean build
--------
or
--------
./mvnw clean package
--------
To
- build the app
- use spring-cloud-contract-stub-runner-spring[Stub Runner Spring] to download the stub of `Http Server`
- run the tests against stubbed server

View File

@@ -0,0 +1,64 @@
buildscript {
repositories {
mavenCentral()
mavenLocal()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
maven { url "http://repo.spring.io/release" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
}
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
mavenLocal()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
maven { url "http://repo.spring.io/release" }
}
apply plugin: 'groovy'
apply plugin: 'org.springframework.boot'
apply plugin: 'maven-publish'
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:$BOM_VERSION"
}
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-actuator")
testCompile "org.springframework.cloud:spring-cloud-starter-contract-stub-runner"
}
test {
systemProperty 'spring.profiles.active', 'gradle'
testLogging {
exceptionFormat = 'full'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.14'
}
task resolveDependencies {
doLast {
project.rootProject.allprojects.each { subProject ->
subProject.buildscript.configurations.each { configuration ->
configuration.resolve()
}
subProject.configurations.each { configuration ->
configuration.resolve()
}
}
}
}

View File

@@ -0,0 +1,2 @@
org.gradle.daemon=false
BOM_VERSION=Edgware.BUILD-SNAPSHOT

View File

@@ -0,0 +1,6 @@
#Fri Aug 19 15:39:05 CEST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip

164
samples/standalone/yml/http-client/gradlew vendored Executable file
View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

234
samples/standalone/yml/http-client/mvnw vendored Executable file
View File

@@ -0,0 +1,234 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
#
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
# for the new JDKs provided by Oracle.
#
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
#
# Oracle JDKs
#
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
#
# Apple JDKs
#
export JAVA_HOME=`/usr/libexec/java_home`
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Migwn, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
local basedir=$(pwd)
local wdir=$(pwd)
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
wdir=$(cd "$wdir/.."; pwd)
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS

View File

@@ -0,0 +1,145 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

View File

@@ -0,0 +1,210 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>http-client-yml</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring Cloud Contract Verifier Http Client Sample</name>
<description>Spring Cloud Contract Verifier Http Client Sample</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath />
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud-dependencies.version>Edgware.BUILD-SNAPSHOT</spring-cloud-dependencies.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- tag::stub_runner[] -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
<scope>test</scope>
</dependency>
<!-- end::stub_runner[] -->
</dependencies>
<!-- tag::contract_bom[] -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- end::contract_bom[] -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<filesets>
<fileset>
<directory>build</directory>
</fileset>
<fileset>
<directory>target</directory>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<profiles>
<profile>
<id>integration</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>gradle</id>
<phase>test</phase>
<configuration>
<executable>./gradlew</executable>
<arguments>
<argument>clean</argument>
<argument>build</argument>
<argument>publishToMavenLocal</argument>
<argument>-PverifierVersion=${spring-cloud-contract.version}</argument>
</arguments>
</configuration>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>windows</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>gradle</id>
<phase>test</phase>
<configuration>
<executable>gradlew.bat</executable>
<arguments>
<argument>clean</argument>
<argument>build</argument>
<argument>publishToMavenLocal</argument>
<argument>-PverifierVersion=${spring-cloud-contract.version}</argument>
</arguments>
</configuration>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1 @@
rootProject.name = 'http-client-yml-gradle'

View File

@@ -0,0 +1,13 @@
package com.example.loan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,93 @@
package com.example.loan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.example.loan.model.FraudCheckStatus;
import com.example.loan.model.FraudServiceRequest;
import com.example.loan.model.FraudServiceResponse;
import com.example.loan.model.LoanApplication;
import com.example.loan.model.LoanApplicationResult;
import com.example.loan.model.LoanApplicationStatus;
import com.example.loan.model.Response;
@Service
public class LoanApplicationService {
private final RestTemplate restTemplate;
private int port = 6565;
@Autowired
public LoanApplicationService(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
public LoanApplicationResult loanApplication(LoanApplication loanApplication) {
FraudServiceRequest request =
new FraudServiceRequest(loanApplication);
FraudServiceResponse response =
sendRequestToFraudDetectionService(request);
return buildResponseFromFraudResult(response);
}
private FraudServiceResponse sendRequestToFraudDetectionService(
FraudServiceRequest request) {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
// tag::client_call_server[]
ResponseEntity<FraudServiceResponse> response =
restTemplate.exchange("http://localhost:" + port + "/fraudcheck", HttpMethod.PUT,
new HttpEntity<>(request, httpHeaders),
FraudServiceResponse.class);
// end::client_call_server[]
return response.getBody();
}
private LoanApplicationResult buildResponseFromFraudResult(FraudServiceResponse response) {
LoanApplicationStatus applicationStatus = null;
if (FraudCheckStatus.OK == response.getFraudCheckStatus()) {
applicationStatus = LoanApplicationStatus.LOAN_APPLIED;
} else if (FraudCheckStatus.FRAUD == response.getFraudCheckStatus()) {
applicationStatus = LoanApplicationStatus.LOAN_APPLICATION_REJECTED;
}
return new LoanApplicationResult(applicationStatus, response.getRejectionReason());
}
public int countAllFrauds() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ResponseEntity<Response> response =
restTemplate.exchange("http://localhost:" + port + "/frauds", HttpMethod.GET,
new HttpEntity<>(httpHeaders),
Response.class);
return response.getBody().getCount();
}
public int countDrunks() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ResponseEntity<Response> response =
restTemplate.exchange("http://localhost:" + port + "/drunks", HttpMethod.GET,
new HttpEntity<>(httpHeaders),
Response.class);
return response.getBody().getCount();
}
public void setPort(int port) {
this.port = port;
}
}

View File

@@ -0,0 +1,21 @@
package com.example.loan.model;
public class Client {
private String pesel;
public Client() {
}
public Client(String pesel) {
this.pesel = pesel;
}
public String getPesel() {
return pesel;
}
public void setPesel(String pesel) {
this.pesel = pesel;
}
}

View File

@@ -0,0 +1,5 @@
package com.example.loan.model;
public enum FraudCheckStatus {
OK, FRAUD
}

View File

@@ -0,0 +1,37 @@
package com.example.loan.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.math.BigDecimal;
public class FraudServiceRequest {
@JsonProperty("client.id")
private String clientId;
private BigDecimal loanAmount;
public FraudServiceRequest() {
}
public FraudServiceRequest(LoanApplication loanApplication) {
this.clientId = loanApplication.getClient().getPesel();
this.loanAmount = loanApplication.getAmount();
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public BigDecimal getLoanAmount() {
return loanAmount;
}
public void setLoanAmount(BigDecimal loanAmount) {
this.loanAmount = loanAmount;
}
}

View File

@@ -0,0 +1,30 @@
package com.example.loan.model;
import com.fasterxml.jackson.annotation.JsonProperty;
public class FraudServiceResponse {
private FraudCheckStatus fraudCheckStatus;
@JsonProperty("rejection.reason")
private String rejectionReason;
public FraudServiceResponse() {
}
public FraudCheckStatus getFraudCheckStatus() {
return fraudCheckStatus;
}
public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) {
this.fraudCheckStatus = fraudCheckStatus;
}
public String getRejectionReason() {
return rejectionReason;
}
public void setRejectionReason(String rejectionReason) {
this.rejectionReason = rejectionReason;
}
}

View File

@@ -0,0 +1,44 @@
package com.example.loan.model;
import java.math.BigDecimal;
public class LoanApplication {
private Client client;
private BigDecimal amount;
private String loanApplicationId;
public LoanApplication() {
}
public LoanApplication(Client client, double amount) {
this.client = client;
this.amount = BigDecimal.valueOf(amount);
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public String getLoanApplicationId() {
return loanApplicationId;
}
public void setLoanApplicationId(String loanApplicationId) {
this.loanApplicationId = loanApplicationId;
}
}

View File

@@ -0,0 +1,32 @@
package com.example.loan.model;
public class LoanApplicationResult {
private LoanApplicationStatus loanApplicationStatus;
private String rejectionReason;
public LoanApplicationResult() {
}
public LoanApplicationResult(LoanApplicationStatus loanApplicationStatus, String rejectionReason) {
this.loanApplicationStatus = loanApplicationStatus;
this.rejectionReason = rejectionReason;
}
public LoanApplicationStatus getLoanApplicationStatus() {
return loanApplicationStatus;
}
public void setLoanApplicationStatus(LoanApplicationStatus loanApplicationStatus) {
this.loanApplicationStatus = loanApplicationStatus;
}
public String getRejectionReason() {
return rejectionReason;
}
public void setRejectionReason(String rejectionReason) {
this.rejectionReason = rejectionReason;
}
}

View File

@@ -0,0 +1,5 @@
package com.example.loan.model;
public enum LoanApplicationStatus {
LOAN_APPLIED, LOAN_APPLICATION_REJECTED
}

View File

@@ -0,0 +1,20 @@
package com.example.loan.model;
public class Response {
private int count;
public Response(int count) {
this.count = count;
}
public Response() {
}
public int getCount() {
return this.count;
}
public void setCount(int count) {
this.count = count;
}
}

View File

@@ -0,0 +1 @@
server.port: 8090

View File

@@ -0,0 +1,54 @@
package com.example.loan;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.loan.model.Client;
import com.example.loan.model.LoanApplication;
import com.example.loan.model.LoanApplicationResult;
import com.example.loan.model.LoanApplicationStatus;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE, properties="server.context-path=/app")
@AutoConfigureStubRunner(ids = {"com.example:http-server-yml:+:stubs:6565"}, workOffline = true)
@DirtiesContext
public class LoanApplicationServiceContextPathTests {
@Autowired
private LoanApplicationService service;
@Test
public void shouldSuccessfullyApplyForLoan() {
// given:
LoanApplication application = new LoanApplication(new Client("1234567890"),
123.123);
// when:
LoanApplicationResult loanApplication = service.loanApplication(application);
// then:
assertThat(loanApplication.getLoanApplicationStatus())
.isEqualTo(LoanApplicationStatus.LOAN_APPLIED);
assertThat(loanApplication.getRejectionReason()).isNull();
}
@Test
public void shouldBeRejectedDueToAbnormalLoanAmount() {
// given:
LoanApplication application = new LoanApplication(new Client("1234567890"),
99999);
// when:
LoanApplicationResult loanApplication = service.loanApplication(application);
// then:
assertThat(loanApplication.getLoanApplicationStatus())
.isEqualTo(LoanApplicationStatus.LOAN_APPLICATION_REJECTED);
assertThat(loanApplication.getRejectionReason()).isEqualTo("Amount too high");
}
}

View File

@@ -0,0 +1,74 @@
package com.example.loan;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.loan.model.Client;
import com.example.loan.model.LoanApplication;
import com.example.loan.model.LoanApplicationResult;
import com.example.loan.model.LoanApplicationStatus;
import static org.assertj.core.api.Assertions.assertThat;
// tag::autoconfigure_stubrunner[]
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = {"com.example:http-server-yml:+:stubs:6565"}, workOffline = true)
@DirtiesContext
public class LoanApplicationServiceTests {
// end::autoconfigure_stubrunner[]
@Autowired
private LoanApplicationService service;
@Test
public void shouldSuccessfullyApplyForLoan() {
// given:
LoanApplication application = new LoanApplication(new Client("1234567890"),
123.123);
// when:
LoanApplicationResult loanApplication = service.loanApplication(application);
// then:
assertThat(loanApplication.getLoanApplicationStatus())
.isEqualTo(LoanApplicationStatus.LOAN_APPLIED);
assertThat(loanApplication.getRejectionReason()).isNull();
}
// tag::client_tdd[]
@Test
public void shouldBeRejectedDueToAbnormalLoanAmount() {
// given:
LoanApplication application = new LoanApplication(new Client("1234567890"),
99999);
// when:
LoanApplicationResult loanApplication = service.loanApplication(application);
// then:
assertThat(loanApplication.getLoanApplicationStatus())
.isEqualTo(LoanApplicationStatus.LOAN_APPLICATION_REJECTED);
assertThat(loanApplication.getRejectionReason()).isEqualTo("Amount too high");
}
// end::client_tdd[]
@Test
public void shouldSuccessfullyGetAllFrauds() {
// when:
int count = service.countAllFrauds();
// then:
assertThat(count).isEqualTo(200);
}
@Test
public void shouldSuccessfullyGetAllDrunks() {
// when:
int count = service.countDrunks();
// then:
assertThat(count).isEqualTo(100);
}
}

View File

@@ -0,0 +1,3 @@
stubrunner:
work-offline: true
stubs.ids: 'com.example:http-server-dsl-gradle:+:stubs:6565'

View File

@@ -0,0 +1,3 @@
stubrunner:
ids: 'com.example:http-server-dsl:+:stubs:8080'
repositoryRoot: http://repo.spring.io/libs-snapshot

View File

@@ -0,0 +1,2 @@
server:
port: 6565

View File

@@ -0,0 +1,6 @@
target/
.gradle
build/

View File

@@ -0,0 +1 @@
-Xmx1024m -XX:MaxPermSize=256m -Djava.awt.headless=true

View File

@@ -0,0 +1 @@
-T2

Binary file not shown.

View File

@@ -0,0 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip

View File

@@ -0,0 +1,20 @@
= Http Server
Run
[source=groovy]
--------
./gradlew clean build publishToMavenLocal
--------
or
--------
./mvnw clean install
--------
To
- build the app
- generate and run Spring Cloud Contract Verifier tests
- publish the fatJar and the stubs to Maven Local

View File

@@ -0,0 +1,78 @@
// tag::repos[]
buildscript {
repositories {
mavenCentral()
mavenLocal()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
maven { url "http://repo.spring.io/release" }
}
// end::repos[]
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE"
classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}"
}
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
// tag::deps_repos[]
repositories {
mavenCentral()
mavenLocal()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
maven { url "http://repo.spring.io/release" }
}
// end::deps_repos[]
apply plugin: 'groovy'
apply plugin: 'org.springframework.boot'
apply plugin: 'spring-cloud-contract'
apply plugin: 'maven-publish'
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:$BOM_VERSION"
}
}
contracts {
packageWithBaseClasses = 'com.example.fraud'
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-actuator")
testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}
test {
systemProperty 'spring.profiles.active', 'gradle'
testLogging {
exceptionFormat = 'full'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.14'
}
clean.doFirst {
delete "~/.m2/repository/com/example/http-server-dsl-gradle"
}
task resolveDependencies {
doLast {
project.rootProject.allprojects.each { subProject ->
subProject.buildscript.configurations.each { configuration ->
configuration.resolve()
}
subProject.configurations.each { configuration ->
configuration.resolve()
}
}
}
}

View File

@@ -0,0 +1,3 @@
org.gradle.daemon=false
verifierVersion=1.2.2.BUILD-SNAPSHOT
BOM_VERSION=Edgware.BUILD-SNAPSHOT

View File

@@ -0,0 +1,6 @@
#Fri Aug 19 15:38:58 CEST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip

164
samples/standalone/yml/http-server/gradlew vendored Executable file
View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

234
samples/standalone/yml/http-server/mvnw vendored Executable file
View File

@@ -0,0 +1,234 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
#
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
# for the new JDKs provided by Oracle.
#
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
#
# Oracle JDKs
#
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
#
# Apple JDKs
#
export JAVA_HOME=`/usr/libexec/java_home`
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Migwn, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
local basedir=$(pwd)
local wdir=$(pwd)
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
wdir=$(cd "$wdir/.."; pwd)
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS

View File

@@ -0,0 +1,145 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

View File

@@ -0,0 +1,256 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>http-server-yml</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring Cloud Contract Verifier Http Server Sample</name>
<description>Spring Cloud Contract Verifier Http Server Sample</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath />
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud-contract.version>1.2.2.BUILD-SNAPSHOT</spring-cloud-contract.version>
<spring-cloud-dependencies.version>Edgware.BUILD-SNAPSHOT</spring-cloud-dependencies.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- tag::verifier_test_dependencies[] -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-verifier</artifactId>
<scope>test</scope>
</dependency>
<!-- end::verifier_test_dependencies[] -->
</dependencies>
<!-- tag::contract_bom[] -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- end::contract_bom[] -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- tag::contract_maven_plugin[] -->
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<packageWithBaseClasses>com.example.fraud</packageWithBaseClasses>
</configuration>
</plugin>
<!-- end::contract_maven_plugin[] -->
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<filesets>
<fileset>
<directory>build</directory>
</fileset>
<fileset>
<directory>target</directory>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<versionRange>[${spring-cloud-contract.version},)</versionRange>
<goals>
<goal>convert</goal>
<goal>generateTests</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<!-- tag::repos[] -->
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<!-- end::repos[] -->
<profiles>
<profile>
<id>integration</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>gradle</id>
<phase>test</phase>
<configuration>
<executable>./gradlew</executable>
<arguments>
<argument>clean</argument>
<argument>build</argument>
<argument>publishToMavenLocal</argument>
<argument>-PverifierVersion=${spring-cloud-contract.version}</argument>
</arguments>
</configuration>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>windows</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>gradle</id>
<phase>test</phase>
<configuration>
<executable>gradlew.bat</executable>
<arguments>
<argument>clean</argument>
<argument>build</argument>
<argument>publishToMavenLocal</argument>
<argument>-PverifierVersion=${spring-cloud-contract.version}</argument>
</arguments>
</configuration>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1 @@
rootProject.name = 'http-server-yml-gradle'

View File

@@ -0,0 +1,17 @@
package com.example.fraud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,40 @@
package com.example.fraud;
import java.math.BigDecimal;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.fraud.model.FraudCheck;
import com.example.fraud.model.FraudCheckResult;
import com.example.fraud.model.FraudCheckStatus;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
@RestController
public class FraudDetectionController {
private static final String NO_REASON = null;
private static final String AMOUNT_TOO_HIGH = "Amount too high";
private static final BigDecimal MAX_AMOUNT = new BigDecimal("5000");
// tag::server_api[]
@RequestMapping(value = "/fraudcheck", method = PUT)
public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
// end::server_api[]
// tag::new_impl[]
if (amountGreaterThanThreshold(fraudCheck)) {
return new FraudCheckResult(FraudCheckStatus.FRAUD, AMOUNT_TOO_HIGH);
}
// end::new_impl[]
// tag::initial_impl[]
return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
// end::initial_impl[]
}
private boolean amountGreaterThanThreshold(FraudCheck fraudCheck) {
return MAX_AMOUNT.compareTo(fraudCheck.getLoanAmount()) < 0;
}
}

View File

@@ -0,0 +1,69 @@
package com.example.fraud;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Marcin Grzejszczak
*/
@RestController
class FraudNameController {
private final FraudVerifier fraudVerifier;
FraudNameController(FraudVerifier fraudVerifier) {
this.fraudVerifier = fraudVerifier;
}
@PutMapping(value = "/frauds/name")
public NameResponse checkByName(@RequestBody NameRequest request) {
boolean fraud = this.fraudVerifier.isFraudByName(request.getName());
if (fraud) {
return new NameResponse("Sorry " + request.getName() + " but you're a fraud");
}
return new NameResponse("Don't worry " + request.getName() + " you're not a fraud");
}
}
interface FraudVerifier {
boolean isFraudByName(String name);
}
class NameRequest {
private String name;
public NameRequest(String name) {
this.name = name;
}
public NameRequest() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
class NameResponse {
private String result;
public NameResponse(String result) {
this.result = result;
}
public NameResponse() {
}
public String getResult() {
return this.result;
}
public void setResult(String result) {
this.result = result;
}
}

View File

@@ -0,0 +1,51 @@
package com.example.fraud;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FraudStatsController {
private final StatsProvider statsProvider;
public FraudStatsController(StatsProvider statsProvider) {
this.statsProvider = statsProvider;
}
@GetMapping(value = "/frauds")
public Response countAllFrauds() {
return new Response(this.statsProvider.count(FraudType.ALL));
}
@GetMapping(value = "/drunks")
public Response countAllDrunks() {
return new Response(this.statsProvider.count(FraudType.DRUNKS));
}
}
enum FraudType {
DRUNKS, ALL
}
interface StatsProvider {
int count(FraudType fraudType);
}
class Response {
private int count;
public Response(int count) {
this.count = count;
}
public Response() {
}
public int getCount() {
return this.count;
}
public void setCount(int count) {
this.count = count;
}
}

View File

@@ -0,0 +1,32 @@
package com.example.fraud.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.math.BigDecimal;
public class FraudCheck {
@JsonProperty("client.id")
private String clientId;
private BigDecimal loanAmount;
public FraudCheck() {
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public BigDecimal getLoanAmount() {
return loanAmount;
}
public void setLoanAmount(BigDecimal loanAmount) {
this.loanAmount = loanAmount;
}
}

View File

@@ -0,0 +1,35 @@
package com.example.fraud.model;
import com.fasterxml.jackson.annotation.JsonProperty;
public class FraudCheckResult {
private FraudCheckStatus fraudCheckStatus;
@JsonProperty("rejection.reason")
private String rejectionReason;
public FraudCheckResult() {
}
public FraudCheckResult(FraudCheckStatus fraudCheckStatus, String rejectionReason) {
this.fraudCheckStatus = fraudCheckStatus;
this.rejectionReason = rejectionReason;
}
public FraudCheckStatus getFraudCheckStatus() {
return fraudCheckStatus;
}
public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) {
this.fraudCheckStatus = fraudCheckStatus;
}
public String getRejectionReason() {
return rejectionReason;
}
public void setRejectionReason(String rejectionReason) {
this.rejectionReason = rejectionReason;
}
}

View File

@@ -0,0 +1,5 @@
package com.example.fraud.model;
public enum FraudCheckStatus {
OK, FRAUD
}

View File

@@ -0,0 +1 @@
server.port=0

View File

@@ -0,0 +1,29 @@
package com.example.fraud;
import org.junit.Before;
import io.restassured.module.mockmvc.RestAssuredMockMvc;
public class FraudBase {
@Before
public void setup() {
RestAssuredMockMvc.standaloneSetup(new FraudDetectionController(),
new FraudStatsController(stubbedStatsProvider()));
}
private StatsProvider stubbedStatsProvider() {
return fraudType -> {
switch (fraudType) {
case DRUNKS:
return 100;
case ALL:
return 200;
}
return 0;
};
}
public void assertThatRejectionReasonIsNull(Object rejectionReason) {
assert rejectionReason == null;
}
}

View File

@@ -0,0 +1,17 @@
package com.example.fraud;
import org.junit.Before;
import io.restassured.module.mockmvc.RestAssuredMockMvc;
public class FraudnameBase {
private static final String FRAUD_NAME = "fraud";
FraudVerifier fraudVerifier = FRAUD_NAME::equals;
@Before
public void setup() {
RestAssuredMockMvc.standaloneSetup(new FraudNameController(this.fraudVerifier));
}
}

View File

@@ -0,0 +1,52 @@
request: # (1)
method: PUT # (2)
url: /fraudcheck # (3)
body: # (4)
"client.id": 1234567890
loanAmount: 99999
headers: # (5)
Content-Type: application/json
matchers:
body:
- path: $.['client.id'] # (6)
type: by_regex
value: "[0-9]{10}"
response: # (7)
status: 200 # (8)
body: # (9)
fraudCheckStatus: "FRAUD"
"rejection.reason": "Amount too high"
headers: # (10)
Content-Type: application/json;charset=UTF-8
#From the Consumer perspective, when shooting a request in the integration test:
#
#(1) - If the consumer sends a request
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `clientId`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
#(7) - then the response will be sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json`
#
#From the Producer perspective, in the autogenerated producer-side test:
#
#(1) - A request will be sent to the producer
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `clientId` `1234567890`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(7) - then the test will assert if the response has been sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`

View File

@@ -0,0 +1,25 @@
request:
method: PUT
url: /fraudcheck
body:
"client.id": 1234567890
loanAmount: 123.123
headers:
Content-Type: application/json
matchers:
body:
- path: $.['client.id']
type: by_regex
value: "[0-9]{10}"
response:
status: 200
body:
fraudCheckStatus: "OK"
"rejection.reason": null
headers:
Content-Type: application/json;charset=UTF-8
matchers:
body:
- path: $.['rejection.reason']
type: by_command
value: assertThatRejectionReasonIsNull($it)

View File

@@ -0,0 +1,21 @@
---
name: "should count all frauds"
request:
method: GET
url: /frauds
response:
status: 200
body:
count: 200
headers:
Content-Type: application/json;charset=UTF-8
---
request:
method: GET
url: /drunks
response:
status: 200
body:
count: 100
headers:
Content-Type: application/json;charset=UTF-8

View File

@@ -0,0 +1,15 @@
# highest priority
priority: 1
request:
method: PUT
url: /frauds/name
body:
name: "fraud"
headers:
Content-Type: application/json;charset=UTF-8
response:
status: 200
body:
result: "Sorry {{{ jsonpath this '$.name' }}} but you're a fraud"
headers:
Content-Type: "{{{ request.headers.Content-Type.0 }}}"

View File

@@ -0,0 +1,18 @@
request:
method: PUT
url: /frauds/name
body:
name: "non fraud"
headers:
Content-Type: application/json
matchers:
body:
- path: $.name
type: by_regex
predefined: only_alpha_unicode
response:
status: 200
body:
result: "Don't worry {{{ jsonpath this '$.name' }}} you're not a fraud"
headers:
Content-Type: "{{{ request.headers.Content-Type.0 }}};charset=UTF-8"

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-samples-standalone</artifactId>
<version>1.2.2.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<artifactId>spring-cloud-contract-samples-yml</artifactId>
<packaging>pom</packaging>
<name>Spring Cloud Contract Standalone Dsl Test Samples</name>
<description>Spring Cloud Contract Standalone Test Samples used for end to end tests</description>
<properties>
<spring-cloud-contract.version>1.2.2.BUILD-SNAPSHOT</spring-cloud-contract.version>
</properties>
<modules>
<module>http-server</module>
<module>http-client</module>
</modules>
<build>
<plugins>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -31,7 +31,8 @@ import groovy.transform.ToString
class Body extends DslProperty {
Body(Map<String, DslProperty> body) {
super(extractValue(body, { DslProperty p -> p.clientValue}), extractValue(body, {DslProperty p -> p.serverValue}))
super(extractValue(body, { p -> p instanceof DslProperty ? ((DslProperty) p).clientValue : p }),
extractValue(body, { p -> p instanceof DslProperty ? ((DslProperty) p).serverValue : p }))
}
private static Map<String, Object> extractValue(Map<String, DslProperty> body, Closure valueProvider) {

View File

@@ -15,7 +15,7 @@ import groovy.transform.ToString
@ToString(includeFields = true, includePackage = false)
class BodyMatchers {
private final RegexPatterns regexPatterns = new RegexPatterns()
private final List<BodyMatcher> jsonPathRegexMatchers = []
protected final List<BodyMatcher> jsonPathRegexMatchers = []
void jsonPath(String path, MatchingTypeValue matchingType) {
this.jsonPathRegexMatchers << new JsonPathBodyMatcher(path, matchingType)

View File

@@ -91,6 +91,18 @@ class Headers {
} as Map<String , Object>
}
/**
* Converts the headers into their stub side representations and returns as
* a map of String key => Object value.
*/
Map<String , Object> asTestSideMap() {
def acc = [:].withDefault { [] as Collection<Object> }
return entries.inject(acc as Map<String, Object>) { Map<String, Object> map, Header header ->
map[header.name] = header.serverValue
return map
} as Map<String , Object>
}
boolean equals(o) {
if (this.is(o)) return true
if (getClass() != o.class) return false

View File

@@ -94,6 +94,15 @@ then:
// end::description[]
}
def 'should set a name'() {
given:
// tag::name[]
org.springframework.cloud.contract.spec.Contract.make {
name("some_special_name")
}
// end::name[]
}
def 'should mark a contract ignored'() {
given:
// tag::ignored[]

View File

@@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.spec.ContractConverter;
import org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStub;
import org.springframework.cloud.contract.verifier.converter.YamlContractConverter;
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter;
import org.springframework.core.io.support.SpringFactoriesLoader;
@@ -165,11 +166,15 @@ class StubRepository {
BasicFileAttributes attrs) throws IOException {
File file = path.toFile();
ContractConverter converter = contractConverter(file);
if (isContractDescriptor(file) && isStubPerConsumerPathMatching(file)) {
contractDescriptors
.addAll(ContractVerifierDslConverter.convertAsCollection(file.getParentFile(), file));
} else if (converter != null && isStubPerConsumerPathMatching(file)) {
contractDescriptors.addAll(converter.convertFrom(file));
if (isStubPerConsumerPathMatching(file)) {
if (isContractDescriptor(file)) {
contractDescriptors
.addAll(ContractVerifierDslConverter.convertAsCollection(file.getParentFile(), file));
} else if (YamlContractConverter.INSTANCE.isAccepted(file)) {
contractDescriptors.addAll(YamlContractConverter.INSTANCE.convertFrom(file));
} else if (converter != null) {
contractDescriptors.addAll(converter.convertFrom(file));
}
}
return super.visitFile(path, attrs);
}

View File

@@ -23,7 +23,7 @@ ext {
]
}
project.version = findProperty('verifierVersion') ?: '1.5.4.RELEASE'
project.version = findProperty('verifierVersion') ?: '1.5.9.RELEASE'
apply plugin: 'groovy'
apply from: "$rootDir/gradle/release.gradle"
apply plugin: 'eclipse'

View File

@@ -20,7 +20,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE")
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE")
}
}

View File

@@ -20,7 +20,7 @@ buildscript {
mavenLocal()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE")
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE")
}
}

View File

@@ -20,7 +20,7 @@ buildscript {
mavenLocal()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE")
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE")
}
}

View File

@@ -40,7 +40,9 @@ import org.codehaus.plexus.archiver.jar.JarArchiver;
public class GenerateStubsMojo extends AbstractMojo {
private static final String STUB_MAPPING_FILE_PATTERN = "**/*.json";
private static final String CONTRACT_FILE_PATTERN = "**/*.groovy";
private static final String GROOVY_CONTRACT_FILE_PATTERN = "**/*.groovy";
private static final String YAML_CONTRACT_FILE_PATTERN = "**/*.yaml";
private static final String YML_CONTRACT_FILE_PATTERN = "**/*.yml";
@Parameter(defaultValue = "${project.build.directory}", readonly = true,
required = true)
@@ -110,7 +112,10 @@ public class GenerateStubsMojo extends AbstractMojo {
try {
if (this.attachContracts) {
this.archiver.addDirectory(stubsOutputDir,
new String[] { STUB_MAPPING_FILE_PATTERN, CONTRACT_FILE_PATTERN },
new String[] { STUB_MAPPING_FILE_PATTERN,
GROOVY_CONTRACT_FILE_PATTERN,
YAML_CONTRACT_FILE_PATTERN,
YML_CONTRACT_FILE_PATTERN },
excludedFilesEmpty() ? new String[0] : this.excludedFiles);
}
else {
@@ -134,7 +139,9 @@ public class GenerateStubsMojo extends AbstractMojo {
private String[] excludes() {
List<String> excludes = new ArrayList<>();
excludes.add(CONTRACT_FILE_PATTERN);
excludes.add(GROOVY_CONTRACT_FILE_PATTERN);
excludes.add(YAML_CONTRACT_FILE_PATTERN);
excludes.add(YML_CONTRACT_FILE_PATTERN);
if (!excludedFilesEmpty()) {
excludes.addAll(Arrays.asList(this.excludedFiles));
}

View File

@@ -28,7 +28,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<version>1.5.9.RELEASE</version>
</parent>
<dependencies>

View File

@@ -28,7 +28,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<version>1.5.9.RELEASE</version>
</parent>
<dependencies>

View File

@@ -28,7 +28,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<version>1.5.9.RELEASE</version>
</parent>
<dependencies>

View File

@@ -21,6 +21,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
@@ -81,6 +85,10 @@
<groupId>com.github.jknack</groupId>
<artifactId>handlebars</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
@@ -105,11 +113,6 @@
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>

View File

@@ -187,7 +187,6 @@ class ClassPresenceChecker {
boolean isClassPresent(String className) {
try {
Class.forName(className)
return true
} catch (ClassNotFoundException e) {

View File

@@ -63,12 +63,24 @@ class MethodBuilder {
static String methodName(ContractMetadata contract, File stubsFile, Contract stubContent) {
if (stubContent.name) {
return NamesUtil.camelCase(NamesUtil.convertIllegalPackageChars(stubContent.name))
String name = NamesUtil.camelCase(NamesUtil.convertIllegalPackageChars(stubContent.name))
if (log.isDebugEnabled()) {
log.debug("Overriding the default test name with [" + name + "]")
}
return name
} else if (contract.convertedContract.size() > 1) {
int index = contract.convertedContract.findIndexOf { it == stubContent}
return "${camelCasedMethodFromFileName(stubsFile)}_${index}"
String name = "${camelCasedMethodFromFileName(stubsFile)}_${index}"
if (log.isDebugEnabled()) {
log.debug("Scenario found. The method name will be [" + name + "]")
}
return name
}
return camelCasedMethodFromFileName(stubsFile)
String name = camelCasedMethodFromFileName(stubsFile)
if (log.isDebugEnabled()) {
log.debug("The method name will be [" + name + "]")
}
return name
}
private static String camelCasedMethodFromFileName(File stubsFile) {

View File

@@ -0,0 +1,177 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.converter
import groovy.transform.CompileStatic
/**
* YAML representation of a {@link org.springframework.cloud.contract.spec.Contract}
*
* @since 1.2.1
* @author Marcin Grzejszczak
*/
@CompileStatic
class YamlContract {
public Request request
public Response response
public Input input
public OutputMessage outputMessage
public String description
public String label
public String name
public Integer priority
public boolean ignored
@CompileStatic
static class Request {
public String method
public String url
public String urlPath
public Map<String, Object> queryParameters = [:]
public Map<String, Object> headers = [:]
public Object body
public String bodyFromFile
public StubMatchers matchers = new StubMatchers()
public Multipart multipart
}
@CompileStatic
static class Multipart {
public Map<String, String> params = [:]
public List<Named> named = []
}
@CompileStatic
static class Named {
public String paramName
public String fileName
public String fileContent
}
@CompileStatic
static class StubMatchers {
public List<BodyStubMatcher> body = []
public List<KeyValueMatcher> headers = []
public MultipartStubMatcher multipart
}
@CompileStatic
static class BodyStubMatcher {
public String path
public StubMatcherType type
public String value
public PredefinedRegex predefined
}
@CompileStatic
static class MultipartStubMatcher {
public List<KeyValueMatcher> params = []
public List<MultipartNamedStubMatcher> named = []
}
@CompileStatic
static class MultipartNamedStubMatcher {
public String paramName
public ValueMatcher fileName
public ValueMatcher fileContent
}
@CompileStatic
static class ValueMatcher {
public String regex
public PredefinedRegex predefined
}
@CompileStatic
static class BodyTestMatcher {
public String path
public TestMatcherType type
public String value
public Integer minOccurrence
public Integer maxOccurrence
public PredefinedRegex predefined
}
@CompileStatic
static class KeyValueMatcher {
public String key
public String regex
public PredefinedRegex predefined
}
@CompileStatic
static class TestHeaderMatcher {
public String key
public String regex
public String command
public PredefinedRegex predefined
}
@CompileStatic
static enum PredefinedRegex {
only_alpha_unicode, number, any_boolean, ip_address, hostname,
email, url, uuid, iso_date, iso_date_time, iso_time,
iso_8601_with_offset, non_empty, non_blank
}
@CompileStatic
static enum StubMatcherType {
by_date, by_time, by_timestamp, by_regex, by_equality
}
@CompileStatic
static enum TestMatcherType {
by_date, by_time, by_timestamp, by_regex, by_equality, by_type, by_command
}
@CompileStatic
static class Response {
public int status
public Map<String, Object> headers = [:]
public Object body
public String bodyFromFile
public TestMatchers matchers = new TestMatchers()
public Boolean async
}
@CompileStatic
static class TestMatchers {
public List<BodyTestMatcher> body = []
public List<TestHeaderMatcher> headers = []
}
@CompileStatic
static class Input {
public String messageFrom
public String triggeredBy
public Map<String, Object> messageHeaders = [:]
public Object messageBody
public String messageBodyFromFile
public String assertThat
public StubMatchers matchers = new StubMatchers()
}
@CompileStatic
static class OutputMessage {
public String sentTo
public Map<String, Object> headers = [:]
public Object body
public String bodyFromFile
public String assertThat
public TestMatchers matchers = new TestMatchers()
}
}

View File

@@ -0,0 +1,556 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.converter
import java.nio.file.Files
import java.util.regex.Pattern
import com.fasterxml.jackson.databind.ObjectMapper
import groovy.transform.CompileStatic
import org.yaml.snakeyaml.Yaml
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.ContractConverter
import org.springframework.cloud.contract.spec.internal.BodyMatcher
import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Headers
import org.springframework.cloud.contract.spec.internal.MatchingType
import org.springframework.cloud.contract.spec.internal.MatchingTypeValue
import org.springframework.cloud.contract.spec.internal.NamedProperty
import org.springframework.cloud.contract.spec.internal.RegexPatterns
import org.springframework.cloud.contract.verifier.converter.YamlContract.BodyStubMatcher
import org.springframework.cloud.contract.verifier.converter.YamlContract.BodyTestMatcher
import org.springframework.cloud.contract.verifier.converter.YamlContract.Input
import org.springframework.cloud.contract.verifier.converter.YamlContract.Named
import org.springframework.cloud.contract.verifier.converter.YamlContract.OutputMessage
import org.springframework.cloud.contract.verifier.converter.YamlContract.PredefinedRegex
import org.springframework.cloud.contract.verifier.converter.YamlContract.Request
import org.springframework.cloud.contract.verifier.converter.YamlContract.Response
import org.springframework.cloud.contract.verifier.converter.YamlContract.KeyValueMatcher
import org.springframework.cloud.contract.verifier.converter.YamlContract.StubMatcherType
import org.springframework.cloud.contract.verifier.converter.YamlContract.StubMatchers
import org.springframework.cloud.contract.verifier.converter.YamlContract.TestHeaderMatcher
import org.springframework.cloud.contract.verifier.converter.YamlContract.TestMatcherType
import org.springframework.cloud.contract.verifier.util.MapConverter
/**
* Simple converter from and to a {@link YamlContract} to a collection of {@link Contract}
*
* @since 1.2.1
* @author Marcin Grzejszczak
*/
@CompileStatic
class YamlContractConverter implements ContractConverter<List<YamlContract>> {
public static final YamlContractConverter INSTANCE = new YamlContractConverter()
@Override
boolean isAccepted(File file) {
String name = file.getName()
return name.endsWith(".yml") || name.endsWith(".yaml")
}
@Override
Collection<Contract> convertFrom(File contractFile) {
ClassLoader classLoader = YamlContractConverter.getClassLoader()
ObjectMapper mapper = new ObjectMapper()
try {
return new Yaml().loadAll(Files.newInputStream(contractFile.toPath())).collect {
YamlContract yamlContract = mapper.convertValue(it, YamlContract.class);
Thread.currentThread().setContextClassLoader(updatedClassLoader(contractFile.getParentFile(), classLoader))
return Contract.make {
if (yamlContract.description) description(yamlContract.description)
if (yamlContract.label) label(yamlContract.label)
if (yamlContract.name) name(yamlContract.name)
if (yamlContract.priority) priority(yamlContract.priority)
if (yamlContract.ignored) ignored()
if (yamlContract.request?.method) {
request {
method(yamlContract.request?.method)
if (yamlContract.request?.url) {
url(yamlContract.request?.url) {
if (yamlContract.request.queryParameters) {
queryParameters {
yamlContract.request.queryParameters.each { String key, Object value ->
if (value instanceof List) {
((List) value).each {
parameter(key, it)
}
} else {
parameter(key, value)
}
}
}
}
}
}
if (yamlContract.request?.urlPath) {
urlPath(yamlContract.request?.urlPath) {
if (yamlContract.request.queryParameters) {
queryParameters {
yamlContract.request.queryParameters.each { String key, Object value ->
if (value instanceof List) {
((List) value).each {
parameter(key, it)
}
} else {
parameter(key, value)
}
}
}
}
}
}
if (yamlContract.request?.headers) {
headers {
yamlContract.request?.headers?.each { String key, Object value ->
KeyValueMatcher matcher = yamlContract.request.matchers.headers.find { it.key == key }
if (value instanceof List) {
((List) value).each {
Object clientValue = clientValue(it, matcher, key)
header(key, new DslProperty(clientValue, it))
}
} else {
Object clientValue = clientValue(value, matcher, key)
header(key, new DslProperty(clientValue, value))
}
}
}
}
if (yamlContract.request.body) body(yamlContract.request.body)
if (yamlContract.request.bodyFromFile) body(file(yamlContract.request.bodyFromFile))
if (yamlContract.request.multipart) {
Map multipartMap = [:]
Map<String, DslProperty> multiPartParams = yamlContract.request.multipart.params.collectEntries { String paramKey, String paramValue ->
KeyValueMatcher matcher = yamlContract.request.matchers.multipart.params.find {
it.key == paramKey
}
Object value = paramValue
if (matcher) {
value = matcher.regex ? Pattern.compile(matcher.regex) :
predefinedToPattern(matcher.predefined)
}
return [(paramKey), new DslProperty<>(value, paramValue)]
} as Map<String, DslProperty>
multipartMap.putAll(multiPartParams)
yamlContract.request.multipart.named.each { Named namedParam ->
YamlContract.MultipartNamedStubMatcher matcher = yamlContract.request.matchers.multipart.named.find {
it.paramName == namedParam.paramName
}
Object fileNameValue = namedParam.fileName
Object fileContentValue = namedParam.fileContent
if (matcher && matcher.fileName) {
fileNameValue = matcher.fileName.regex ? Pattern.compile(matcher.fileName.regex) :
predefinedToPattern(matcher.fileName.predefined)
}
if (matcher && matcher.fileContent) {
fileContentValue = matcher.fileContent.regex ? Pattern.compile(matcher.fileContent.regex) :
predefinedToPattern(matcher.fileContent.predefined)
}
multipartMap.put(namedParam.paramName, new NamedProperty(new DslProperty<>(fileNameValue, namedParam.fileName),
new DslProperty<>(fileContentValue, namedParam.fileContent)))
}
multipart(multipartMap)
}
stubMatchers {
yamlContract.request.matchers?.body?.each { BodyStubMatcher matcher ->
MatchingTypeValue value = null
switch (matcher.type) {
case StubMatcherType.by_date:
value = byDate()
break
case StubMatcherType.by_time:
value = byTime()
break
case StubMatcherType.by_timestamp:
value = byTimestamp()
break
case StubMatcherType.by_regex:
String regex = matcher.value
if (matcher.predefined) {
regex = predefinedToPattern(matcher.predefined).pattern()
}
value = byRegex(regex)
break
case StubMatcherType.by_equality:
value = byEquality()
break
}
jsonPath(matcher.path, value)
}
}
}
response {
status(yamlContract.response.status)
headers {
yamlContract.response?.headers?.each { String key, Object value ->
TestHeaderMatcher matcher = yamlContract.response.matchers.headers.find { it.key == key }
if (value instanceof List) {
((List) value).each {
Object serverValue = serverValue(it, matcher, key)
header(key, new DslProperty(it, serverValue))
}
} else {
Object serverValue = serverValue(value, matcher, key)
header(key, new DslProperty(value, serverValue))
}
}
}
if (yamlContract.response.body) body(yamlContract.response.body)
if (yamlContract.response.bodyFromFile) body(file(yamlContract.response.bodyFromFile))
if (yamlContract.response.async) async()
testMatchers {
yamlContract.response?.matchers?.body?.each { BodyTestMatcher testMatcher ->
MatchingTypeValue value = null
switch (testMatcher.type) {
case TestMatcherType.by_date:
value = byDate()
break
case TestMatcherType.by_time:
value = byTime()
break
case TestMatcherType.by_timestamp:
value = byTimestamp()
break
case TestMatcherType.by_regex:
String regex = testMatcher.value
if (testMatcher.predefined) {
regex = predefinedToPattern(testMatcher.predefined).pattern()
}
value = byRegex(regex)
break
case TestMatcherType.by_equality:
value = byEquality()
break
case TestMatcherType.by_type:
value = byType() {
if (testMatcher.minOccurrence != null) minOccurrence(testMatcher.minOccurrence)
if (testMatcher.maxOccurrence != null) maxOccurrence(testMatcher.maxOccurrence)
}
break
case TestMatcherType.by_command:
value = byCommand(testMatcher.value)
break
}
jsonPath(testMatcher.path, value)
}
}
}
}
if (yamlContract.input) {
input {
if (yamlContract.input.messageFrom) messageFrom(yamlContract.input.messageFrom)
if (yamlContract.input.assertThat) assertThat(yamlContract.input.assertThat)
if (yamlContract.input.triggeredBy) triggeredBy(yamlContract.input.triggeredBy)
messageHeaders {
yamlContract.input?.messageHeaders?.each { String key, Object value ->
KeyValueMatcher matcher = yamlContract.input.matchers?.headers?.find { it.key == key }
Object clientValue = clientValue(value, matcher, key)
header(key, new DslProperty(clientValue, value))
}
}
if (yamlContract.input.messageBody) messageBody(yamlContract.input.messageBody)
if (yamlContract.input.messageBodyFromFile) messageBody(file(yamlContract.input.messageBodyFromFile))
stubMatchers {
yamlContract.input.matchers.body?.each { BodyStubMatcher matcher ->
MatchingTypeValue value = null
switch (matcher.type) {
case StubMatcherType.by_date:
value = byDate()
break
case StubMatcherType.by_time:
value = byTime()
break
case StubMatcherType.by_timestamp:
value = byTimestamp()
break
case StubMatcherType.by_regex:
value = byRegex(matcher.value)
break
case StubMatcherType.by_equality:
value = byEquality()
break
}
jsonPath(matcher.path, value)
}
}
}
}
OutputMessage outputMsg = yamlContract.outputMessage
if (outputMsg) {
outputMessage {
if (outputMsg.assertThat) assertThat(outputMsg.assertThat)
if (outputMsg.sentTo) sentTo(outputMsg.sentTo)
headers {
outputMsg.headers?.each { String key, Object value ->
TestHeaderMatcher matcher = outputMsg.matchers?.headers?.find { it.key == key }
Object serverValue = serverValue(value, matcher, key)
header(key, new DslProperty(value, serverValue))
}
}
if (outputMsg.body) body(outputMsg.body)
if (outputMsg.bodyFromFile) body(file(outputMsg.bodyFromFile))
if (outputMsg.matchers) {
testMatchers {
yamlContract.outputMessage?.matchers?.body?.each { BodyTestMatcher testMatcher ->
MatchingTypeValue value = null
switch (testMatcher.type) {
case TestMatcherType.by_date:
value = byDate()
break
case TestMatcherType.by_time:
value = byTime()
break
case TestMatcherType.by_timestamp:
value = byTimestamp()
break
case TestMatcherType.by_regex:
value = byRegex(testMatcher.value)
break
case TestMatcherType.by_equality:
value = byEquality()
break
case TestMatcherType.by_type:
value = byType() {
if (testMatcher.minOccurrence != null) minOccurrence(testMatcher.minOccurrence)
if (testMatcher.maxOccurrence != null) maxOccurrence(testMatcher.maxOccurrence)
}
break
case TestMatcherType.by_command:
value = byCommand(testMatcher.value)
break
}
jsonPath(testMatcher.path, value)
}
}
}
}
}
}
}
}
catch (FileNotFoundException e) {
throw new IllegalStateException(e)
}
catch (IllegalStateException ise) {
throw ise
}
catch (Exception e1) {
throw new IllegalStateException("Exception occurred while processing the file [" + contractFile + "]", e1)
} finally {
Thread.currentThread().setContextClassLoader(classLoader)
}
}
protected Object serverValue(Object value, TestHeaderMatcher matcher, String key) {
Object serverValue = value
if (matcher?.regex) {
serverValue = Pattern.compile(matcher.regex)
Pattern pattern = (Pattern) serverValue
assertPatternMatched(pattern, value, key)
} else if (matcher?.predefined) {
Pattern pattern = predefinedToPattern(matcher.predefined)
serverValue = pattern
assertPatternMatched(pattern, value, key)
} else if (matcher?.command) {
serverValue = new ExecutionProperty(matcher.command)
}
return serverValue
}
protected Object clientValue(Object value, KeyValueMatcher matcher, String key) {
Object clientValue = value
if (matcher?.regex) {
clientValue = Pattern.compile(matcher.regex)
Pattern pattern = (Pattern) clientValue
assertPatternMatched(pattern, value, key)
} else if (matcher?.predefined) {
Pattern pattern = predefinedToPattern(matcher.predefined)
clientValue = pattern
assertPatternMatched(pattern, value, key)
}
return clientValue
}
private void assertPatternMatched(Pattern pattern, value, String key) {
boolean matches = pattern.matcher(value.toString()).matches()
if (!matches) throw new IllegalStateException("Broken headers! A header with " +
"key [${key}] with value [${value}] is not matched by regex [${pattern.pattern()}]")
}
protected Pattern predefinedToPattern(PredefinedRegex predefinedRegex) {
RegexPatterns patterns = new RegexPatterns()
switch (predefinedRegex) {
case PredefinedRegex.only_alpha_unicode:
return Pattern.compile(patterns.onlyAlphaUnicode())
case PredefinedRegex.number:
return Pattern.compile(patterns.number())
case PredefinedRegex.any_boolean:
return Pattern.compile(patterns.anyBoolean())
case PredefinedRegex.ip_address:
return Pattern.compile(patterns.ipAddress())
case PredefinedRegex.hostname:
return Pattern.compile(patterns.hostname())
case PredefinedRegex.email:
return Pattern.compile(patterns.email())
case PredefinedRegex.url:
return Pattern.compile(patterns.url())
case PredefinedRegex.uuid:
return Pattern.compile(patterns.uuid())
case PredefinedRegex.iso_date:
return Pattern.compile(patterns.isoDate())
case PredefinedRegex.iso_date_time:
return Pattern.compile(patterns.isoDateTime())
case PredefinedRegex.iso_time:
return Pattern.compile(patterns.isoTime())
case PredefinedRegex.iso_8601_with_offset:
return Pattern.compile(patterns.iso8601WithOffset())
case PredefinedRegex.non_empty:
return Pattern.compile(patterns.nonEmpty())
case PredefinedRegex.non_blank:
return Pattern.compile(patterns.nonBlank())
}
}
protected String file(String relativePath) {
URL resource = Thread.currentThread().getContextClassLoader().getResource(relativePath)
if (resource == null) {
throw new IllegalStateException("File [${relativePath}] is not present")
}
return new File(resource.toURI()).text
}
protected static ClassLoader updatedClassLoader(File rootFolder, ClassLoader classLoader) {
ClassLoader urlCl = URLClassLoader
.newInstance([rootFolder.toURI().toURL()] as URL[], classLoader)
Thread.currentThread().setContextClassLoader(urlCl)
return urlCl
}
@Override
List<YamlContract> convertTo(Collection<Contract> contracts) {
return contracts.collect { Contract contract ->
YamlContract yamlContract = new YamlContract()
if (contract?.request?.method) {
yamlContract.request = new Request()
yamlContract.request.with {
method = contract?.request?.method?.clientValue
url = contract?.request?.url?.clientValue
headers = (contract?.request?.headers as Headers)?.asTestSideMap()
body = MapConverter.getTestSideValues(contract?.request?.body)
matchers = new StubMatchers()
contract?.request?.matchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
matchers.body << new BodyStubMatcher(
path: matcher.path(),
type: stubMatcherType(matcher.matchingType()),
value: matcher.value()?.toString()
)
}
}
yamlContract.response = new Response()
yamlContract.response.with {
status = contract?.response?.status?.clientValue as Integer
headers = (contract?.response?.headers as Headers)?.asStubSideMap()
body = MapConverter.getStubSideValues(contract?.response?.body)
contract?.response?.matchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
matchers.body << new BodyTestMatcher(
path: matcher.path(),
type: testMatcherType(matcher.matchingType()),
value: matcher.value()?.toString(),
minOccurrence: matcher.minTypeOccurrence(),
maxOccurrence: matcher.maxTypeOccurrence()
)
}
}
}
if (contract.input) {
yamlContract.input = new Input()
yamlContract.input.assertThat = contract?.input?.assertThat?.toString()
yamlContract.input.triggeredBy = contract?.input?.triggeredBy?.toString()
yamlContract.input.messageHeaders = (contract?.input?.messageHeaders as Headers)?.asTestSideMap()
yamlContract.input.messageBody = MapConverter.getTestSideValues(contract?.input?.messageBody)
yamlContract.input.messageFrom = contract?.input?.messageFrom?.serverValue
yamlContract.input.matchers.body.each {
contract?.input?.matchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
yamlContract.input.matchers.body << new BodyStubMatcher(
path: matcher.path(),
type: stubMatcherType(matcher.matchingType()),
value: matcher.value()?.toString()
)
}
}
}
if (contract.outputMessage) {
yamlContract.outputMessage = new OutputMessage()
yamlContract.outputMessage.headers = (contract?.outputMessage?.headers as Headers)?.asStubSideMap()
yamlContract.outputMessage.body = MapConverter.getStubSideValues(contract?.outputMessage?.body)
yamlContract.outputMessage.matchers.body.each {
contract?.input?.matchers?.jsonPathMatchers()?.each { BodyMatcher matcher ->
yamlContract.outputMessage.matchers.body << new BodyTestMatcher(
path: matcher.path(),
type: testMatcherType(matcher.matchingType()),
value: matcher.value()?.toString(),
minOccurrence: matcher.minTypeOccurrence(),
maxOccurrence: matcher.maxTypeOccurrence()
)
}
}
}
return yamlContract
}
}
protected TestMatcherType testMatcherType(MatchingType matchingType) {
switch (matchingType) {
case MatchingType.EQUALITY:
return TestMatcherType.by_equality
case MatchingType.TYPE:
return TestMatcherType.by_type
case MatchingType.COMMAND:
return TestMatcherType.by_command
case MatchingType.DATE:
return TestMatcherType.by_date
case MatchingType.TIME:
return TestMatcherType.by_time
case MatchingType.TIMESTAMP:
return TestMatcherType.by_timestamp
case MatchingType.REGEX:
return TestMatcherType.by_regex
}
return null
}
protected StubMatcherType stubMatcherType(MatchingType matchingType) {
switch (matchingType) {
case MatchingType.EQUALITY:
return StubMatcherType.by_equality
case MatchingType.TYPE:
case MatchingType.COMMAND:
throw new UnsupportedOperationException("No type for client side")
case MatchingType.DATE:
return StubMatcherType.by_date
case MatchingType.TIME:
return StubMatcherType.by_time
case MatchingType.TIMESTAMP:
return StubMatcherType.by_timestamp
case MatchingType.REGEX:
return StubMatcherType.by_regex
}
return null
}
}

Some files were not shown because too many files have changed in this diff Show More