Merge remote-tracking branch 'upstream/master'

Conflicts:
	accurest-core/src/main/groovy/io/codearte/accurest/TestGenerator.groovy
This commit is contained in:
Maciej Zielinski
2015-11-04 09:08:38 +01:00
146 changed files with 8021 additions and 1580 deletions

View File

@@ -1,9 +1,15 @@
language: java
sudo: false
jdk:
- oraclejdk7
- openjdk7
- oraclejdk8
cache:
directories:
- $HOME/.gradle
- $HOME/.m2
install: ./gradlew assemble
script: ./gradlew check --stacktrace --info --continue
script: ./gradlew check funcTest --stacktrace --info --continue

View File

@@ -2,13 +2,20 @@ Accurate REST
=============
[![Build Status](https://travis-ci.org/Codearte/accurest.svg?branch=master)](https://travis-ci.org/Codearte/accurest) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.codearte.accurest/accurest-gradle-plugin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.codearte.accurest/accurest-gradle-plugin)
[![Join the chat at https://gitter.im/Codearte/accurest](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Codearte/accurest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Consumer Driven Contracts verifier for Java
Just to make long story short - AccuREST is a tool for Consumer Driven Contract (CDC) development. AccuREST ships easy DSL for describing REST contracts for JVM-based applications. The contract DSL is used by AccuREST for two things:
To make a long story short - AccuREST is a tool for Consumer Driven Contract (CDC) development. AccuREST ships an easy DSL for describing REST contracts for JVM-based applications. The contract DSL is used by AccuREST for two things:
generating Wiremock's JSON stub definitions, allowing rapid development of the consumer side,
1. generating WireMock's JSON stub definitions, allowing rapid development of the consumer side,
generating Spock's acceptance tests for the server - to verify if your API implementation is compliant with the contract.
By using AccuREST you can move TDD to an architecture level.
2. moving TDD to an architecture level.
For more information please follow to the [Wiki](https://github.com/Codearte/accurest/wiki/1.-Introduction)
For more information please go to the [Wiki](https://github.com/Codearte/accurest/wiki/1.-Introduction)
## Requirements
### Wiremock
In order to use Accurest with Wiremock you have to have __Wiremock in version at least 2.0.0-beta__ . Of course the higher the better :)

View File

@@ -0,0 +1,13 @@
package io.codearte.accurest.wiremock
import groovy.transform.CompileStatic
import io.codearte.accurest.dsl.WireMockStubStrategy
@CompileStatic
class DslToWireMockClientConverter extends DslToWireMockConverter {
@Override
String convertContent(String dslBody) {
return new WireMockStubStrategy(createGroovyDSLfromStringContent(dslBody)).toWireMockClientStub()
}
}

View File

@@ -2,9 +2,10 @@ package io.codearte.accurest.wiremock
import groovy.transform.CompileStatic
import io.codearte.accurest.dsl.GroovyDsl
import org.codehaus.groovy.control.CompilerConfiguration
@CompileStatic
abstract class DslToWiremockConverter implements SingleFileConverter {
abstract class DslToWireMockConverter implements SingleFileConverter {
@Override
boolean canHandleFileName(String fileName) {
@@ -17,6 +18,6 @@ abstract class DslToWiremockConverter implements SingleFileConverter {
}
protected GroovyDsl createGroovyDSLfromStringContent(String groovyDslAsString) {
return (GroovyDsl) new GroovyShell(this.class.classLoader).evaluate("$groovyDslAsString")
return (GroovyDsl) new GroovyShell(this.class.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding:'UTF-8')).evaluate("$groovyDslAsString")
}
}

View File

@@ -1,13 +0,0 @@
package io.codearte.accurest.wiremock
import groovy.transform.CompileStatic
import io.codearte.accurest.dsl.WiremockStubStrategy
@CompileStatic
class DslToWiremockClientConverter extends DslToWiremockConverter {
@Override
String convertContent(String dslBody) {
return new WiremockStubStrategy(createGroovyDSLfromStringContent(dslBody)).toWiremockClientStub()
}
}

View File

@@ -4,6 +4,7 @@ import groovy.io.FileType
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
@@ -28,12 +29,12 @@ class RecursiveFilesConverter {
if (!singleFileConverter.canHandleFileName(sourceFile.name)) {
return
}
String convertedContent = singleFileConverter.convertContent(sourceFile.text)
String convertedContent = singleFileConverter.convertContent(sourceFile.getText(StandardCharsets.UTF_8.toString()))
Path absoluteTargetPath = createAndReturnTargetDirectory(sourceFile)
File newGroovyFile = createTargetFileWithProperName(absoluteTargetPath, sourceFile)
newGroovyFile.text = convertedContent
newGroovyFile.setText(convertedContent, StandardCharsets.UTF_8.toString())
} catch (Exception e) {
throw new ConversionAccurestException("Unable to convertion of ${sourceFile.name}", e)
throw new ConversionAccurestException("Unable to make convertion of ${sourceFile.name}", e)
}
}
}

View File

@@ -1,52 +1,68 @@
package io.codearte.accurest.wiremock
import groovy.io.FileType
import groovy.json.JsonOutput
import groovy.json.JsonParserType
import groovy.json.JsonSlurper
import groovy.xml.XmlUtil
import io.codearte.accurest.dsl.GroovyDsl
import nl.flotsam.xeger.Xeger
import java.nio.charset.StandardCharsets
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava
class WiremockToDslConverter {
static String fromWiremockStub(String wiremockStringStub) {
return new WiremockToDslConverter().convertFromWiremockStub(wiremockStringStub)
class WireMockToDslConverter {
static String fromWireMockStub(String wireMockStringStub) {
return new WireMockToDslConverter().convertFromWireMockStub(wireMockStringStub)
}
private String convertFromWiremockStub(String wiremockStringStub) {
Object wiremockStub = new JsonSlurper().parseText(wiremockStringStub)
def request = wiremockStub.request
def response = wiremockStub.response
private String convertFromWireMockStub(String wireMockStringStub) {
Object wireMockStub = parseStubDefinition(wireMockStringStub)
Integer priority = wireMockStub.priority
def request = wireMockStub.request
def response = wireMockStub.response
def bodyPatterns = request.bodyPatterns
String urlPattern = request.urlPattern
return """\
request {
${request.method ? "method \"\"\"$request.method\"\"\"" : ""}
${request.url ? "url \"\"\"$request.url\"\"\"" : ""}
${request.urlPattern ? "url \$(client(regex('${escapeJava(request.urlPattern)}')), server(''))" : ""}
${request.urlPath ? "url \"\"\"$request.urlPath\"\"\"" : ""}
${
request.headers ? """headers {
${
request.headers.collect {
def assertion = it.value
String headerName = it.key as String
def entry = assertion.entrySet().first()
"""header(\"\"\"$headerName\"\"\", ${buildHeader(entry.key, entry.value)})\n"""
}.join('')
${priority ? "priority ${priority}" : ''}
request {
${request.method ? "method \"\"\"$request.method\"\"\"" : ""}
${request.url ? "url \"\"\"$request.url\"\"\"" : ""}
${urlPattern ? "url \$(client(regex('${escapeJava(urlPattern)}')), server('${new Xeger(escapeJava(urlPattern)).generate()}'))" : ""}
${request.urlPath ? "url \"\"\"$request.urlPath\"\"\"" : ""}
${
request.headers ? """headers {
${
request.headers.collect {
def assertion = it.value
String headerName = it.key as String
def entry = assertion.entrySet().first()
"""header(\"\"\"$headerName\"\"\", ${buildHeader(entry.key, entry.value)})\n"""
}.join('')
}
}
""" : ""
}
${bodyPatterns?.equalTo?.every { it } ? "body('''${bodyPatterns.equalTo[0]}''')" : ''}
${bodyPatterns?.equalToJson?.every { it } ? "body('''${bodyPatterns.equalToJson[0]}''')" : ''}
${bodyPatterns?.matches?.every { it } ? "body \$(client(regex('${escapeJava(bodyPatterns.matches[0])}')), server('${new Xeger(escapeJava(bodyPatterns.matches[0])).generate()}'))" : ""}
}
}
""" : ""
}
}
response {
${response.status ? "status $response.status" : ""}
${response.body ? "body( ${buildBody(response.body)})" : ""}
${
response {
${response.status ? "status $response.status" : ""}
${response.body ? "body( ${buildBody(response.body)})" : ""}
${
response.headers ? """headers {
${response.headers.collect { "header('$it.key': '${it.value}')\n" }.join('')}
}
""" : ""
${response.headers.collect { "header('$it.key': '${it.value}')\n" }.join('')}
}
""" : ""
}
}
"""
}
"""
}
private Object parseStubDefinition(String wireMockStringStub) {
new JsonSlurper().setType(JsonParserType.LAX).parseText(wireMockStringStub)
}
private String buildHeader(String method, Object value) {
@@ -140,11 +156,11 @@ class WiremockToDslConverter {
if (!it.name.endsWith('json')) {
return
}
String dslFromWiremockStub = fromWiremockStub(it.text)
String dslWrappedWithFactoryMethod = wrapWithFactoryMethod(dslFromWiremockStub)
String dslFromWireMockStub = fromWireMockStub(it.getText(StandardCharsets.UTF_8.toString()))
String dslWrappedWithFactoryMethod = wrapWithFactoryMethod(dslFromWireMockStub)
File newGroovyFile = new File(it.parent, it.name.replaceAll('json', 'groovy'))
println("Creating new groovy file [$newGroovyFile.path]")
newGroovyFile.text = dslWrappedWithFactoryMethod
newGroovyFile.setText(dslWrappedWithFactoryMethod, StandardCharsets.UTF_8.toString())
} catch (Exception e) {
System.err.println(e)
}
@@ -152,10 +168,10 @@ class WiremockToDslConverter {
}
}
static String wrapWithFactoryMethod(String dslFromWiremockStub) {
static String wrapWithFactoryMethod(String dslFromWireMockStub) {
return """\
${GroovyDsl.name}.make {
$dslFromWiremockStub
$dslFromWireMockStub
}
"""
}

View File

@@ -0,0 +1,118 @@
/**
* Copyright 2009 Wilfred Springer
* Copyright 2012 Jason Pell
* Copyright 2013 Antonio García-Domínguez
*
* 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.
*
* The class is bundled together with our code because it has not been
* released to any central repository.
*
*/
package nl.flotsam.xeger;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.State;
import dk.brics.automaton.Transition;
import java.util.List;
import java.util.Random;
/**
* An object that will generate text from a regular expression. In a way, it's the opposite of a regular expression
* matcher: an instance of this class will produce text that is guaranteed to match the regular expression passed in.
*/
public class Xeger {
private final Automaton automaton;
private Random random;
/**
* Constructs a new instance, accepting the regular expression and the randomizer.
*
* @param regex The regular expression. (Not <code>null</code>.)
* @param random The object that will randomize the way the String is generated. (Not <code>null</code>.)
* @throws IllegalArgumentException If the regular expression is invalid.
*/
public Xeger(String regex, Random random) {
assert regex != null;
assert random != null;
this.automaton = new RegExp(regex).toAutomaton();
this.random = random;
}
/**
* As {@link nl.flotsam.xeger.Xeger#Xeger(String, java.util.Random)}, creating a {@link java.util.Random} instance
* implicityly.
*
* @param regex as string
*/
public Xeger(String regex) {
this(regex, new Random());
}
/**
* Generates a random String that is guaranteed to match the regular expression passed to the constructor.
* @return generated regexp
*/
public String generate() {
StringBuilder builder = new StringBuilder();
generate(builder, automaton.getInitialState());
return builder.toString();
}
private void generate(StringBuilder builder, State state) {
List<Transition> transitions = state.getSortedTransitions(false);
if (transitions.size() == 0) {
assert state.isAccept();
return;
}
int nroptions = state.isAccept() ? transitions.size() : transitions.size() - 1;
int option = Xeger.getRandomInt(0, nroptions, random);
if (state.isAccept() && option == 0) { // 0 is considered stop
return;
}
// Moving on to next transition
Transition transition = transitions.get(option - (state.isAccept() ? 1 : 0));
appendChoice(builder, transition);
generate(builder, transition.getDest());
}
private void appendChoice(StringBuilder builder, Transition transition) {
char c = (char) Xeger.getRandomInt(transition.getMin(), transition.getMax(), random);
builder.append(c);
}
public Random getRandom() {
return random;
}
public void setRandom(Random random) {
this.random = random;
}
/**
* Generates a random number within the given bounds.
*
* @param min The minimum number (inclusive).
* @param max The maximum number (inclusive).
* @param random The object used as the randomizer.
* @return A random number in the given range.
*/
static int getRandomInt(int min, int max, Random random) {
// Use random.nextInt as it guarantees a uniform distribution
int maxForRandom=max-min+1;
return random.nextInt(maxForRandom) + min;
}
}

View File

@@ -0,0 +1,142 @@
package io.codearte.accurest.wiremock
import org.skyscreamer.jsonassert.JSONAssert
import spock.lang.Specification
class DslToWireMockClientConverterSpec extends Specification {
def "should convert DSL file to WireMock JSON"() {
given:
def converter = new DslToWireMockClientConverter()
and:
String dslBody = """
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method('PUT')
url \$(client(~/\\/[0-9]{2}/), server('/12'))
}
response {
status 200
}
}
"""
when:
String json = converter.convertContent(dslBody)
then:
JSONAssert.assertEquals('''
{"request":{"method":"PUT","urlPattern":"/[0-9]{2}"},"response":{"status":200}}
''', json, false)
}
def "should convert DSL file with a nested list to WireMock JSON"() {
given:
def converter = new DslToWireMockClientConverter()
and:
String dslBody = """
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method 'PUT'
url '/api/12'
headers {
header 'Content-Type': 'application/vnd.com.ofg.twitter-places-analyzer.v1+json'
}
body '''
[{
"created_at": "Sat Jul 26 09:38:57 +0000 2014",
"id": 492967299297845248,
"id_str": "492967299297845248",
"text": "Gonna see you at Warsaw",
"place":
{
"attributes":{},
"bounding_box":
{
"coordinates":
[[
[-77.119759,38.791645],
[-76.909393,38.791645],
[-76.909393,38.995548],
[-77.119759,38.995548]
]],
"type":"Polygon"
},
"country":"United States",
"country_code":"US",
"full_name":"Washington, DC",
"id":"01fbe706f872cb32",
"name":"Washington",
"place_type":"city",
"url": "http://api.twitter.com/1/geo/id/01fbe706f872cb32.json"
}
}]
'''
}
response {
status 200
}
}
"""
when:
String json = converter.convertContent(dslBody)
then:
JSONAssert.assertEquals('''
{
"request" : {
"url" : "/api/12",
"method" : "PUT",
"bodyPatterns" : [ {
"matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.995548)]"
}, {
"matchesJsonPath" : "$[*].place[?(@.country == 'United States')]"
}, {
"matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -77.119759)]"
}, {
"matchesJsonPath" : "$[*].place[?(@.name == 'Washington')]"
}, {
"matchesJsonPath" : "$[*].place.bounding_box[?(@.type == 'Polygon')]"
}, {
"matchesJsonPath" : "$[*][?(@.id_str == '492967299297845248')]"
}, {
"matchesJsonPath" : "$[*].place[?(@.country_code == 'US')]"
}, {
"matchesJsonPath" : "$[*][?(@.id == 492967299297845248)]"
}, {
"matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -76.909393)]"
}, {
"matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]"
}, {
"matchesJsonPath" : "$[*].place[?(@.id == '01fbe706f872cb32')]"
}, {
"matchesJsonPath" : "$[*].place[?(@.url == 'http://api.twitter.com/1/geo/id/01fbe706f872cb32.json')]"
}, {
"matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -77.119759)]"
}, {
"matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == -76.909393)]"
}, {
"matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.995548)]"
}, {
"matchesJsonPath" : "$[*][?(@.text == 'Gonna see you at Warsaw')]"
}, {
"matchesJsonPath" : "$[*].place[?(@.place_type == 'city')]"
}, {
"matchesJsonPath" : "$[*][?(@.created_at == 'Sat Jul 26 09:38:57 +0000 2014')]"
}, {
"matchesJsonPath" : "$[*].place[?(@.full_name == 'Washington, DC')]"
}, {
"matchesJsonPath" : "$[*].place.bounding_box.coordinates[*][*][?(@ == 38.791645)]"
} ],
"headers" : {
"Content-Type" : {
"equalTo" : "application/vnd.com.ofg.twitter-places-analyzer.v1+json"
}
}
},
"response" : {
"status" : 200
}
}
''', json, false)
}
}

View File

@@ -1,101 +0,0 @@
package io.codearte.accurest.wiremock
import groovy.json.JsonSlurper
import spock.lang.Specification
class DslToWiremockClientConverterSpec extends Specification {
def "should convert DSL file to Wiremock JSON"() {
given:
def converter = new DslToWiremockClientConverter()
and:
String dslBody = """
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method('PUT')
url \$(client(~/\\/[0-9]{2}/), server('/12'))
}
response {
status 200
}
}
"""
when:
String json = converter.convertContent(dslBody)
then:
new JsonSlurper().parseText(json) == new JsonSlurper().parseText("""
{"request":{"method":"PUT","urlPattern":"/[0-9]{2}"},"response":{"status":200}}""")
}
def "should convert DSL file with a nested list to Wiremock JSON"() {
given:
def converter = new DslToWiremockClientConverter()
and:
String dslBody = """
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method 'PUT'
url '/api/12'
headers {
header 'Content-Type': 'application/vnd.com.ofg.twitter-places-analyzer.v1+json'
}
body '''
[{
"created_at": "Sat Jul 26 09:38:57 +0000 2014",
"id": 492967299297845248,
"id_str": "492967299297845248",
"text": "Gonna see you at Warsaw",
"place":
{
"attributes":{},
"bounding_box":
{
"coordinates":
[[
[-77.119759,38.791645],
[-76.909393,38.791645],
[-76.909393,38.995548],
[-77.119759,38.995548]
]],
"type":"Polygon"
},
"country":"United States",
"country_code":"US",
"full_name":"Washington, DC",
"id":"01fbe706f872cb32",
"name":"Washington",
"place_type":"city",
"url": "http://api.twitter.com/1/geo/id/01fbe706f872cb32.json"
}
}]
'''
}
response {
status 200
}
}
"""
when:
String json = converter.convertContent(dslBody)
then:
new JsonSlurper().parseText(json) == new JsonSlurper().parseText("""{
"request":{
"method":"PUT",
"url":"/api/12",
"bodyPatterns": [
{ "equalTo": "[{\\"created_at\\":\\"Sat Jul 26 09:38:57 +0000 2014\\",\\"id\\":492967299297845248,\\"id_str\\":\\"492967299297845248\\",\\"place\\":{\\"attributes\\":{},\\"bounding_box\\":{\\"coordinates\\":[[[-77.119759,38.791645],[-76.909393,38.791645],[-76.909393,38.995548],[-77.119759,38.995548]]],\\"type\\":\\"Polygon\\"},\\"country\\":\\"United States\\",\\"country_code\\":\\"US\\",\\"full_name\\":\\"Washington, DC\\",\\"id\\":\\"01fbe706f872cb32\\",\\"name\\":\\"Washington\\",\\"place_type\\":\\"city\\",\\"url\\":\\"http://api.twitter.com/1/geo/id/01fbe706f872cb32.json\\"},\\"text\\":\\"Gonna see you at Warsaw\\"}]" }
],
"headers": {
"Content-Type": {
"equalTo": "application/vnd.com.ofg.twitter-places-analyzer.v1+json"
}
}
},
"response":{
"status":200}
}
""")
}
}

View File

@@ -0,0 +1,542 @@
package io.codearte.accurest.wiremock
import com.github.tomakehurst.wiremock.stubbing.StubMapping
import io.codearte.accurest.dsl.GroovyDsl
import spock.lang.Specification
class WireMockToDslConverterSpec extends Specification {
def 'should produce a Groovy DSL from WireMock stub'() {
given:
String wireMockStub = '''\
{
"request": {
"method": "GET",
"url": "/path",
"headers" : {
"Accept": {
"matches": "text/.*"
},
"X-Custom-Header": {
"contains": "2134"
}
}
},
"response": {
"status": 200,
"body": '{"id": { "value": "132" }, "surname": "Kowalsky", "name": "Jan", "created": "2014-02-02 12:23:43" }',
"headers": {
"Content-Type": "text/plain"
}
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'GET'
url '/path'
headers {
header('Accept': $(
client(regex('text/.*')),
server('text/plain')
))
header('X-Custom-Header': $(
client(regex('^.*2134.*$')),
server('121345')
))
}
}
response {
status 200
body(
id: [value: '132'],
surname: 'Kowalsky',
name: 'Jan',
created: '2014-02-02 12:23:43'
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert WireMock stub with response body containing JSON with escaped double quotes'() {
given:
String wireMockStub = '''\
{
"request": {
"method": "DELETE",
"urlPattern": "/credit-card-verification-data/[0-9]+",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.mymoid-adapter.v2+json; charset=UTF-8"
}
}
},
"response": {
"status": 200,
"body": "{\\"status\\": \\"OK\\"}",
"headers": {
"Content-Type": "application/json"
}
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'DELETE'
url $(client(~/\/credit-card-verification-data\/[0-9]+/), server('/credit-card-verification-data/1'))
headers {
header('Content-Type': 'application/vnd.mymoid-adapter.v2+json; charset=UTF-8')
}
}
response {
status 200
body("""{
"status": "OK"
}""")
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert WireMock stub with response body containing integer'() {
given:
String wireMockStub = '''\
{
"request": {
"method": "POST",
"url": "/charge/count",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.creditcard-reporter.v1+json"
}
}
},
"response": {
"status": 200,
"body": 200,
"headers": {
"Content-Type": "application/json"
}
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/charge/count'
headers {
header('Content-Type': 'application/vnd.creditcard-reporter.v1+json')
}
}
response {
status 200
body(200)
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert WireMock stub with response body as a list'() {
given:
String wireMockStub = '''\
{
"request": {
"method": "POST",
"url": "/charge/count",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.creditcard-reporter.v1+json"
}
}
},
"response": {
"status": 200,
"body": '[ {"a":1, "c":"3"}, "b", "a" ]',
"headers": {
"Content-Type": "application/json"
}
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/charge/count'
headers {
header('Content-Type': 'application/vnd.creditcard-reporter.v1+json')
}
}
response {
status 200
body([
[a: 1, c: '3'],
'b',
'a'
])
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert WireMock stub with response body containing a nested list'() {
given:
String wireMockStub = '''\
{
"request": {
"method": "POST",
"url": "/charge/search?pageNumber=0&size=2147483647",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.creditcard-reporter.v1+json"
}
}
},
"response": {
"status": 200,
"body": '[{"amount":1.01, "name":"Name", "info":{"title":"title1", "payload":null}, "booleanvalue":true, "user":null}, {"amount":2.01, "name":"Name2", "info":{"title":"title2", "payload":null}, "booleanvalue":true, "user":null}]'
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/charge/search?pageNumber=0&size=2147483647'
headers {
header('Content-Type': 'application/vnd.creditcard-reporter.v1+json')
}
}
response {
status 200
body("""[
{
"amount": 1.01,
"name": "Name",
"info": {
"title": "title1",
"payload": null
},
"booleanvalue": true,
"user": null
},
{
"amount": 2.01,
"name": "Name2",
"info": {
"title": "title2",
"payload": null
},
"booleanvalue": true,
"user": null
}
]""")
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert WireMock stub with request body checking equality to Json'() {
given:
String wireMockStub = '''\
{
"request": {
"method": "POST",
"url": "/test",
"bodyPatterns": [{
"equalTo": '{"property1":"abc", "property2":"2017-01", "property3":"666", "property4":1428566412}'
}]
},
"response": {
"status": 200
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/test'
body ('''{"property1":"abc","property2":"2017-01","property3":"666","property4":1428566412}''')
}
response {
status 200
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""")
and:
evaluatedGroovyDsl == expectedGroovyDsl
}
def 'should convert WireMock stub with request body checking matching to Json'() {
given:
String wireMockStub = '''\
{
"request": {
"method": "POST",
"url": "/test",
"bodyPatterns": [{
"matches": "[0-9]{5}"
}]
},
"response": {
"status": 200
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/test'
body $(client(~/[0-9]{5}/), server('12345'))
}
response {
status 200
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""")
and:
evaluatedGroovyDsl == expectedGroovyDsl
}
def 'should convert WireMock stub with request body with equalToJson'() {
given:
String wireMockStub = '''\
{
"request" : {
"url" : "/test",
"method" : "POST",
"bodyPatterns" : [ {
"equalToJson" : '{"pan":"4855141150107894", "expirationDate":"2017-01", "dcvx":"178"}',
"jsonCompareMode" : "LENIENT"
} ]
},
"response" : {
"status" : 200
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/test'
body '''{"pan":"4855141150107894","expirationDate":"2017-01","dcvx":"178"}'''
}
response {
status 200
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""")
and:
evaluatedGroovyDsl == expectedGroovyDsl
}
def 'should convert WireMock stub with request body with equalTo'() {
given:
String wireMockStub = '''\
{
"request" : {
"url" : "/test",
"method" : "POST",
"bodyPatterns" : [ {
"equalTo" : '{"pan":"4855141150107894", "expirationDate":"2017-01", "dcvx":"178"}'
} ]
},
"response" : {
"status" : 200
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/test'
body '''{"pan":"4855141150107894","expirationDate":"2017-01","dcvx":"178"}'''
}
response {
status 200
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""")
and:
evaluatedGroovyDsl == expectedGroovyDsl
}
def 'should convert WireMock stub with request body with matches'() {
given:
String wireMockStub = '''\
{
"request" : {
"url" : "/test",
"method" : "POST",
"bodyPatterns" : [ {
"matches" : "[0-9]{2}"
} ]
},
"response" : {
"status" : 200
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/test'
body $(client(~/[0-9]{2}/), server('12'))
}
response {
status 200
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""")
and:
evaluatedGroovyDsl == expectedGroovyDsl
}
def 'should convert WireMock stub with priorities'() {
given:
String wireMockStub = '''\
{
"priority" : 2,
"request" : {
"url" : "/test",
"method" : "POST"
},
"response" : {
"status" : 200
}
}
'''
and:
stubMappingIsValidWireMockStub(wireMockStub)
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
priority 2
request {
method 'POST'
url '/test'
}
response {
status 200
}
}
when:
String groovyDsl = WireMockToDslConverter.fromWireMockStub(wireMockStub)
then:
GroovyDsl evaluatedGroovyDsl = new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""")
and:
evaluatedGroovyDsl == expectedGroovyDsl
}
void stubMappingIsValidWireMockStub(String mappingDefinition) {
StubMapping.buildFrom(mappingDefinition)
}
}

View File

@@ -1,290 +0,0 @@
package io.codearte.accurest.wiremock
import io.codearte.accurest.dsl.GroovyDsl
import spock.lang.Specification
class WiremockToDslConverterSpec extends Specification {
def 'should produce a Groovy DSL from Wiremock stub'() {
given:
String wiremockStub = '''\
{
"request": {
"method": "GET",
"url": "/path",
"headers" : {
"Accept": {
"matches": "text/.*"
},
"X-Custom-Header": {
"contains": "2134"
}
}
},
"response": {
"status": 200,
"body": "{ \\"id\\": { \\"value\\": \\"132\\" }, \\"surname\\": \\"Kowalsky\\", \\"name\\": \\"Jan\\", \\"created\\": \\"2014-02-02 12:23:43\\" }",
"headers": {
"Content-Type": "text/plain",
}
}
}
'''
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'GET'
url '/path'
headers {
header('Accept': $(
client(regex('text/.*')),
server('text/plain')
))
header('X-Custom-Header': $(
client(regex('^.*2134.*$')),
server('121345')
))
}
}
response {
status 200
body(
id: [value: '132'],
surname: 'Kowalsky',
name: 'Jan',
created: '2014-02-02 12:23:43'
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
when:
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert Wiremock stub with body containing simple JSON'() {
given:
String wiremockStub = '''\
{
"request": {
"method": "DELETE",
"urlPattern": "/credit-card-verification-data/[0-9]+",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.mymoid-adapter.v2+json; charset=UTF-8"
}
}
},
"response": {
"status": 200,
"body": "{\\"status\\": \\"OK\\"}",
"headers": {
"Content-Type": "application/json"
}
}
}
'''
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'DELETE'
url $(client(~/\/credit-card-verification-data\/[0-9]+/), server(''))
headers {
header('Content-Type': 'application/vnd.mymoid-adapter.v2+json; charset=UTF-8')
}
}
response {
status 200
body("""{
"status": "OK"
}""")
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert Wiremock stub with body containing integer'() {
given:
String wiremockStub = '''\
{
"request": {
"method": "POST",
"url": "/charge/count",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.creditcard-reporter.v1+json"
}
}
},
"response": {
"status": 200,
"body": 200,
"headers": {
"Content-Type": "application/json"
}
}
}
'''
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/charge/count'
headers {
header('Content-Type': 'application/vnd.creditcard-reporter.v1+json')
}
}
response {
status 200
body(200)
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert Wiremock stub with body as a list'() {
given:
String wiremockStub = '''\
{
"request": {
"method": "POST",
"url": "/charge/count",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.creditcard-reporter.v1+json"
}
}
},
"response": {
"status": 200,
"body": "[ {\\"a\\":1, \\"c\\":\\"3\\"}, \\"b\\", \\"a\\" ]",
"headers": {
"Content-Type": "application/json"
}
}
}
'''
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/charge/count'
headers {
header('Content-Type': 'application/vnd.creditcard-reporter.v1+json')
}
}
response {
status 200
body([
[a: 1, c: '3'],
'b',
'a'
])
headers {
header 'Content-Type': 'application/json'
}
}
}
when:
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
def 'should convert Wiremock stub with body containing a nested list'() {
given:
String wiremockStub = '''\
{
"request": {
"method": "POST",
"url": "/charge/search?pageNumber=0&size=2147483647",
"headers": {
"Content-Type": {
"equalTo": "application/vnd.creditcard-reporter.v1+json"
}
}
},
"response": {
"status": 200,
"body":"[{\\"amount\\":1.01,\\"name\\":\\"Name\\",\\"info\\":{\\"title\\":\\"title1\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null},{\\"amount\\":2.01,\\"name\\":\\"Name2\\",\\"info\\":{\\"title\\":\\"title2\\",\\"payload\\":null},\\"booleanvalue\\":true,\\"user\\":null}]"
}
}
'''
and:
GroovyDsl expectedGroovyDsl = GroovyDsl.make {
request {
method 'POST'
url '/charge/search?pageNumber=0&size=2147483647'
headers {
header('Content-Type': 'application/vnd.creditcard-reporter.v1+json')
}
}
response {
status 200
body("""[
{
"amount": 1.01,
"name": "Name",
"info": {
"title": "title1",
"payload": null
},
"booleanvalue": true,
"user": null
},
{
"amount": 2.01,
"name": "Name2",
"info": {
"title": "title2",
"payload": null
},
"booleanvalue": true,
"user": null
}
]""")
}
}
when:
String groovyDsl = WiremockToDslConverter.fromWiremockStub(wiremockStub)
then:
new GroovyShell(this.class.classLoader).evaluate(
""" io.codearte.accurest.dsl.GroovyDsl.make {
$groovyDsl
}""") == expectedGroovyDsl
}
}

View File

@@ -0,0 +1,64 @@
/**
* Copyright 2009 Wilfred Springer
* Copyright 2012 Jason Pell
* Copyright 2013 Antonio García-Domínguez
*
* 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 nl.flotsam.xeger;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class XegerTest {
@Test
public void shouldGenerateTextCorrectly() {
String regex = "[ab]{4,6}c";
Xeger generator = new Xeger(regex);
for (int i = 0; i < 100; i++) {
String text = generator.generate();
assertTrue(text.matches(regex));
}
}
@Test
public void testRepeatableRegex() {
for (int x = 0; x < 1000; x++) {
Xeger generator = new Xeger("[ab]{4,6}c", new Random(1000));
Xeger generator2 = new Xeger("[ab]{4,6}c", new Random(1000));
List<String> firstRegexList = generateRegex(generator, 100);
List<String> secondRegexList = generateRegex(generator2, 100);
for (int i = 0; i < firstRegexList.size(); i++) {
assertEquals("Index mismatch: " + i, firstRegexList.get(i),
secondRegexList.get(i));
}
}
}
private List<String> generateRegex(Xeger generator, int count) {
List<String> regexList = new ArrayList<String>();
for (int i = 0; i < count; i++) {
regexList.add(generator.generate());
}
return regexList;
}
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright 2009 Wilfred Springer
* Copyright 2012 Jason Pell
* Copyright 2013 Antonio García-Domínguez
*
* 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 nl.flotsam.xeger;
import org.hamcrest.Matchers;
import org.junit.Test;
import java.util.Random;
import static org.junit.Assert.assertThat;
public class XegerUtilsTest {
@Test
public void shouldGenerateRandomNumberCorrectly() {
Random random = new Random();
for (int i = 0; i < 100; i++) {
int number = Xeger.getRandomInt(3, 7, random);
assertThat(number, Matchers.greaterThanOrEqualTo(3));
assertThat(number, Matchers.lessThanOrEqualTo(7));
}
}
}

View File

@@ -11,6 +11,7 @@ import static io.codearte.accurest.builder.MethodBuilder.createTestMethod
import static io.codearte.accurest.util.NamesUtil.capitalize
class SingleTestGenerator {
private final AccurestConfigProperties configProperties
SingleTestGenerator(AccurestConfigProperties configProperties) {
@@ -34,7 +35,9 @@ class SingleTestGenerator {
}
}
if (configProperties.testMode == TestMode.MOCKMVC) {
if (configProperties.testMode == TestMode.JAXRSCLIENT) {
clazz.addStaticImport('javax.ws.rs.client.Entity.*')
} else if (configProperties.testMode == TestMode.MOCKMVC) {
clazz.addStaticImport('com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*')
} else {
clazz.addStaticImport('com.jayway.restassured.RestAssured.*')
@@ -47,14 +50,23 @@ class SingleTestGenerator {
}
if (configProperties.ruleClassForTests) {
clazz.addImport('org.junit.Rule')
.addRule(configProperties.ruleClassForTests)
.addRule(configProperties.ruleClassForTests)
}
addJsonPathRelatedImports(clazz)
listOfFiles.each {
clazz.addMethod(createTestMethod(it, configProperties.targetFramework))
clazz.addMethod(createTestMethod(it, configProperties))
}
return clazz.build()
}
private ClassBuilder addJsonPathRelatedImports(ClassBuilder clazz) {
clazz.addImport(['com.jayway.jsonpath.DocumentContext',
'com.jayway.jsonpath.JsonPath',
'net.minidev.json.JSONArray'])
}
}

View File

@@ -4,6 +4,7 @@ import io.codearte.accurest.config.AccurestConfigProperties
import org.apache.commons.io.FilenameUtils
import org.codehaus.plexus.util.DirectoryScanner
import java.nio.charset.StandardCharsets
import java.util.concurrent.atomic.AtomicInteger
import static io.codearte.accurest.util.NamesUtil.afterLast
@@ -66,19 +67,19 @@ class TestGenerator {
if (filesToClass.size()) {
def className = afterLast(includedDirectoryRelativePath, File.separator) + configProperties.targetFramework.classNameSuffix
def packageName = buildPackage(packageNameForClass, includedDirectoryRelativePath)
def classBytes = generator.buildClass(filesToClass, className, packageName).bytes
def classBytes = generator.buildClass(filesToClass, className, packageName).getBytes(StandardCharsets.UTF_8)
saver.saveClassFile(className, packageName, classBytes)
counter.incrementAndGet()
}
}
}
private static String buildPackage(final String packageNameForClass, final String includedDirectoryRelativePath) {
String directory = beforeLast(includedDirectoryRelativePath, File.separator)
return "$packageNameForClass.${directoryToPackage(directory)}"
}
private static String buildPackage(final String packageNameForClass, final String includedDirectoryRelativePath) {
String directory = beforeLast(includedDirectoryRelativePath, File.separator)
return "$packageNameForClass.${directoryToPackage(directory)}"
}
private static String normalizePath(String path) {
return FilenameUtils.separatorsToUnix(path)
}
private static String normalizePath(String path) {
return FilenameUtils.separatorsToUnix(path)
}
}

View File

@@ -43,6 +43,11 @@ class ClassBuilder {
return this
}
ClassBuilder addImport(List<String> importsToAdd) {
imports.addAll(importsToAdd)
return this
}
ClassBuilder addStaticImport(String importToAdd) {
staticImports << importToAdd
return this

View File

@@ -0,0 +1,95 @@
package io.codearte.accurest.builder
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import io.codearte.accurest.dsl.GroovyDsl
import io.codearte.accurest.dsl.internal.Header
import io.codearte.accurest.dsl.internal.QueryParameter
@PackageScope
@TypeChecked
class JaxRsClientSpockMethodBodyBuilder extends SpockMethodBodyBuilder {
JaxRsClientSpockMethodBodyBuilder(GroovyDsl stubDefinition) {
super(stubDefinition)
}
@Override
protected void givenBlock(BlockBuilder bb) {
}
@Override
protected void when(BlockBuilder bb) {
bb.addLine("def response = webTarget")
bb.indent()
appendUrlPathAndQueryParameters(bb)
appendRequestWithRequiredResponseContentType(bb)
appendHeaders(bb)
appendMethodAndBody(bb)
bb.unindent()
bb.addEmptyLine()
bb.addLine("String responseAsString = response.readEntity(String)")
}
protected void appendRequestWithRequiredResponseContentType(BlockBuilder bb) {
String acceptHeader = getHeader("Accept")
if (acceptHeader) {
bb.addLine(".request('$acceptHeader')")
} else {
bb.addLine(".request()")
}
}
protected void appendUrlPathAndQueryParameters(BlockBuilder bb) {
if (request.url) {
bb.addLine(".path('$request.url.serverValue')")
} else if (request.urlPath) {
bb.addLine(".path('$request.urlPath.serverValue')")
request.urlPath.queryParameters?.parameters.findAll(this.&allowedQueryParameter).each { QueryParameter param ->
bb.addLine(".queryParam('$param.name', '${resolveParamValue(param).toString()}')")
}
}
}
protected void appendMethodAndBody(BlockBuilder bb) {
String method = request.method.serverValue.toString().toLowerCase()
if (request.body) {
String contentType = getHeader('Content-Type') ?: getRequestContentType().mimeType
bb.addLine(".method('$method', entity('$bodyAsString', '$contentType'))")
} else {
bb.addLine(".method('$method')")
}
}
protected appendHeaders(BlockBuilder bb) {
request.headers?.collect { Header header ->
if (header.name == 'Content-Type' || header.name == 'Accept') return // Particular headers are set via 'request' / 'entity' methods
bb.addLine(".header('${header.name}', '${header.serverValue}')")
}
}
protected String getHeader(String name) {
return request.headers?.entries.find { it.name == name }?.serverValue
}
@Override
protected void validateResponseCodeBlock(BlockBuilder bb) {
bb.addLine("response.status == $response.status.serverValue")
}
@Override
protected void validateResponseHeadersBlock(BlockBuilder bb) {
response.headers?.collect { Header header ->
bb.addLine("response.getHeaderString('$header.name') == '$header.serverValue'")
}
}
@Override
protected String getResponseAsString() {
return 'responseAsString'
}
}

View File

@@ -1,9 +1,12 @@
package io.codearte.accurest.builder
import groovy.util.logging.Slf4j
import io.codearte.accurest.config.AccurestConfigProperties
import io.codearte.accurest.config.TestFramework
import io.codearte.accurest.config.TestMode
import io.codearte.accurest.dsl.GroovyDsl
import io.codearte.accurest.util.NamesUtil
import org.codehaus.groovy.control.CompilerConfiguration
/**
* @author Jakub Kubrynski
@@ -13,28 +16,36 @@ class MethodBuilder {
private final String methodName
private final GroovyDsl stubContent
private final TestFramework lang
private final AccurestConfigProperties configProperties
private MethodBuilder(String methodName, GroovyDsl stubContent, TestFramework lang) {
private MethodBuilder(String methodName, GroovyDsl stubContent, AccurestConfigProperties configProperties) {
this.stubContent = stubContent
this.methodName = methodName
this.lang = lang
this.configProperties = configProperties
}
static MethodBuilder createTestMethod(File stubsFile, TestFramework lang) {
static MethodBuilder createTestMethod(File stubsFile, AccurestConfigProperties configProperties) {
log.debug("Stub content from file [${stubsFile.text}]")
GroovyDsl stubContent = new GroovyShell(this.classLoader).evaluate(stubsFile)
GroovyDsl stubContent = new GroovyShell(this.classLoader, new Binding(), new CompilerConfiguration(sourceEncoding:'UTF-8')).evaluate(stubsFile)
log.debug("Stub content Groovy DSL [$stubContent]")
String methodName = NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator)))
return new MethodBuilder(methodName, stubContent, lang)
return new MethodBuilder(methodName, stubContent, configProperties)
}
void appendTo(BlockBuilder blockBuilder) {
if (lang == TestFramework.JUNIT) {
if (configProperties.targetFramework == TestFramework.JUNIT) {
blockBuilder.addLine('@Test')
}
blockBuilder.addLine(lang.methodModifier + "$methodName() {")
new SpockMethodBodyBuilder(stubContent).appendTo(blockBuilder)
blockBuilder.addLine(configProperties.targetFramework.methodModifier + "$methodName() {")
getMethodBodyBuilder().appendTo(blockBuilder)
blockBuilder.addLine('}')
}
private SpockMethodBodyBuilder getMethodBodyBuilder() {
if (configProperties.testMode == TestMode.JAXRSCLIENT) {
return new JaxRsClientSpockMethodBodyBuilder(stubContent)
}
return new MockMvcSpockMethodBodyBuilder(stubContent)
}
}

View File

@@ -0,0 +1,90 @@
package io.codearte.accurest.builder
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import groovy.transform.TypeCheckingMode
import io.codearte.accurest.dsl.GroovyDsl
import io.codearte.accurest.dsl.internal.Header
import io.codearte.accurest.dsl.internal.QueryParameter
import io.codearte.accurest.dsl.internal.Request
import io.codearte.accurest.dsl.internal.UrlPath
import java.util.regex.Pattern
@PackageScope
@TypeChecked
class MockMvcSpockMethodBodyBuilder extends SpockMethodBodyBuilder {
MockMvcSpockMethodBodyBuilder(GroovyDsl stubDefinition) {
super(stubDefinition)
}
protected void given(BlockBuilder bb) {
bb.addLine('def request = given()')
bb.indent()
request.headers?.collect { Header header ->
bb.addLine(".header('${getTestSideValue(header.name)}', '${getTestSideValue(header.serverValue)}')")
}
if (request.body) {
bb.addLine(".body('$bodyAsString')")
}
bb.unindent()
}
protected void when(BlockBuilder bb) {
bb.addLine('def response = given().spec(request)')
bb.indent()
String url = buildUrl(request)
String method = request.method.serverValue.toString().toLowerCase()
bb.addLine(/.${method}("$url")/)
bb.unindent()
}
protected void validateResponseCodeBlock(BlockBuilder bb) {
bb.addLine("response.statusCode == $response.status.serverValue")
}
protected void validateResponseHeadersBlock(BlockBuilder bb) {
response.headers?.collect { Header header ->
bb.addLine("response.header('$header.name') ${convertHeaderComparison(header.serverValue)}")
}
}
private String convertHeaderComparison(Object headerValue) {
return " == '$headerValue'"
}
private String convertHeaderComparison(Pattern headerValue) {
return "==~ java.util.regex.Pattern.compile('$headerValue')"
}
@Override
protected String getResponseAsString() {
return 'response.body.asString()'
}
protected String buildUrl(Request request) {
if (request.url)
return getTestSideValue(request.url.serverValue)
if (request.urlPath)
return getTestSideValue(buildUrlFromUrlPath(request.urlPath))
throw new IllegalStateException("URL is not set!")
}
@TypeChecked(TypeCheckingMode.SKIP)
protected String buildUrlFromUrlPath(UrlPath urlPath) {
String params = ""
if (urlPath.queryParameters) {
params = urlPath.queryParameters.parameters
.findAll(this.&allowedQueryParameter)
.inject([] as List<String>) { List<String> result, QueryParameter param ->
result << "${param.name}=${resolveParamValue(param).toString()}"
}
.join('&')
}
return "$urlPath.serverValue?$params"
}
}

View File

@@ -1,104 +1,216 @@
package io.codearte.accurest.builder
import groovy.json.JsonOutput
import groovy.json.StringEscapeUtils
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import io.codearte.accurest.dsl.GroovyDsl
import io.codearte.accurest.dsl.internal.ExecutionProperty
import io.codearte.accurest.dsl.internal.Header
import java.util.regex.Pattern
import io.codearte.accurest.dsl.internal.*
import io.codearte.accurest.util.ContentType
import io.codearte.accurest.util.MapConverter
import io.codearte.accurest.util.JsonToJsonPathsConverter
import io.codearte.accurest.util.JsonPaths
import static io.codearte.accurest.util.ContentUtils.*
/**
* @author Jakub Kubrynski
*/
@PackageScope
class SpockMethodBodyBuilder {
private final GroovyDsl stubDefinition
@TypeChecked
abstract class SpockMethodBodyBuilder {
private static final Boolean TEST_SIDE = false
protected final Request request
protected final Response response
SpockMethodBodyBuilder(GroovyDsl stubDefinition) {
this.stubDefinition = stubDefinition
this.request = stubDefinition.request
this.response = stubDefinition.response
}
void appendTo(BlockBuilder blockBuilder) {
blockBuilder.startBlock()
blockBuilder.addLine('given:').startBlock()
blockBuilder.addLine('def request = given()')
blockBuilder.indent()
stubDefinition.request.headers?.collect { Header header ->
blockBuilder.addLine(".header('${header.name}', '${header.serverValue}')")
}
if (stubDefinition.request.body) {
String matches = new JsonOutput().toJson(stubDefinition.request.body.serverValue)
blockBuilder.addLine(".body('$matches')")
}
blockBuilder.unindent().endBlock().addEmptyLine()
blockBuilder.addLine('when:').startBlock()
blockBuilder.addLine('def response = given().spec(request)')
blockBuilder.indent()
blockBuilder.addLine(".${stubDefinition.request.method.serverValue.toLowerCase()}(\"$stubDefinition.request.url.serverValue\")")
blockBuilder.unindent().endBlock().addEmptyLine()
blockBuilder.addLine('then:').startBlock()
blockBuilder.addLine("response.statusCode == $stubDefinition.response.status.serverValue")
stubDefinition.response.headers?.collect { Header header ->
blockBuilder.addLine("response.header('$header.name') == '$header.serverValue'")
}
if (stubDefinition.response.body) {
blockBuilder.endBlock()
blockBuilder.addLine('and:').startBlock()
blockBuilder.addLine('def responseBody = new JsonSlurper().parseText(response.body.asString())')
def responseBody = stubDefinition.response.body.serverValue
if (responseBody instanceof List) {
processArrayElements(responseBody, "", blockBuilder)
} else {
processMapElement(responseBody, blockBuilder, "")
}
}
blockBuilder.endBlock()
givenBlock(blockBuilder)
whenBlock(blockBuilder)
thenBlock(blockBuilder)
blockBuilder.endBlock()
}
private void processBodyElement(BlockBuilder blockBuilder, String rootProperty, def element) {
def value = element.value
String property = rootProperty + "." + element.key
if (value instanceof String) {
if (value.startsWith('$')) {
value = value.substring(1).replaceAll('\\$value', "responseBody$property")
blockBuilder.addLine(value)
} else {
blockBuilder.addLine("responseBody$property == \"${value}\"")
}
} else if (value instanceof Map) {
processMapElement(value, blockBuilder, property)
} else if (value instanceof List) {
processArrayElements(value, property, blockBuilder)
} else if (value instanceof Pattern) {
blockBuilder.addLine("responseBody$property ==~ java.util.regex.Pattern.compile('${value}')")
} else if (value instanceof ExecutionProperty) {
ExecutionProperty exec = (ExecutionProperty) value
blockBuilder.addLine("${exec.insertValue("responseBody$property")}")
} else {
blockBuilder.addLine("responseBody$property == ${value}")
protected void thenBlock(BlockBuilder bb) {
bb.addLine('then:')
bb.startBlock()
then(bb)
bb.endBlock()
}
protected void whenBlock(BlockBuilder bb) {
bb.addLine('when:')
bb.startBlock()
when(bb)
bb.endBlock().addEmptyLine()
}
protected void givenBlock(BlockBuilder bb) {
bb.addLine('given:')
bb.startBlock()
given(bb)
bb.endBlock().addEmptyLine()
}
protected void given(BlockBuilder bb) {}
protected abstract void when(BlockBuilder bb)
protected abstract void validateResponseCodeBlock(BlockBuilder bb)
protected abstract void validateResponseHeadersBlock(BlockBuilder bb)
protected abstract String getResponseAsString()
protected void then(BlockBuilder bb) {
validateResponseCodeBlock(bb)
if (response.headers) {
validateResponseHeadersBlock(bb)
}
if (response.body) {
bb.endBlock()
bb.addLine('and:').startBlock()
validateResponseBodyBlock(bb)
}
}
private void processMapElement(def value, BlockBuilder blockBuilder, String property) {
value.each { entry -> processBodyElement(blockBuilder, property, entry) }
}
private void processArrayElements(List responseBody, String property, BlockBuilder blockBuilder) {
responseBody.eachWithIndex {
listElement, listIndex ->
listElement.each {
entry -> processBodyElement(blockBuilder, property + "[$listIndex]", entry)
protected void validateResponseBodyBlock(BlockBuilder bb) {
def responseBody = response.body.serverValue
ContentType contentType = getResponseContentType()
if (responseBody instanceof GString) {
responseBody = extractValue(responseBody, contentType, { DslProperty dslProperty -> dslProperty.serverValue })
}
if (contentType == ContentType.JSON) {
appendJsonPath(bb, responseAsString)
JsonPaths jsonPaths = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(responseBody)
jsonPaths.each {
it.buildJsonPathComparison('parsedJson').each {
bb.addLine(it)
}
}
processBodyElement(bb, "", responseBody)
} else if (contentType == ContentType.XML) {
bb.addLine("def responseBody = new XmlSlurper().parseText($responseAsString)")
// TODO xml validation
} else {
bb.addLine("def responseBody = ($responseAsString)")
processText(bb, "", responseBody as String)
}
}
private void processClosure(Closure value, BlockBuilder blockBuilder, String property) {
blockBuilder.addLine()
protected void processText(BlockBuilder blockBuilder, String property, String value) {
if (value.startsWith('$')) {
value = value.substring(1).replaceAll('\\$value', "responseBody$property")
blockBuilder.addLine(value)
} else {
blockBuilder.addLine("responseBody$property == \"${value}\"")
}
}
protected String
protected String getBodyAsString() {
Object bodyValue = extractServerValueFromBody(request.body.serverValue)
String json = new JsonOutput().toJson(bodyValue)
json = convertUnicodeEscapes(json)
return trimRepeatedQuotes(json)
}
protected String convertUnicodeEscapes(String json) {
return StringEscapeUtils.unescapeJavaScript(json)
}
protected String trimRepeatedQuotes(String toTrim) {
return toTrim.startsWith('"') ? toTrim.replaceAll('"', '') : toTrim
}
protected Object extractServerValueFromBody(bodyValue) {
if (bodyValue instanceof GString) {
bodyValue = extractValue(bodyValue, { DslProperty dslProperty -> dslProperty.serverValue })
} else {
bodyValue = MapConverter.transformValues(bodyValue, { it instanceof DslProperty ? it.serverValue : it })
}
return bodyValue
}
protected boolean allowedQueryParameter(QueryParameter param) {
return allowedQueryParameter(param.serverValue)
}
protected boolean allowedQueryParameter(MatchingStrategy matchingStrategy) {
return matchingStrategy.type != MatchingStrategy.Type.ABSENT
}
protected boolean allowedQueryParameter(Object o) {
return true
}
protected String resolveParamValue(QueryParameter param) {
return resolveParamValue(param.serverValue)
}
protected String resolveParamValue(Object value) {
return value.toString()
}
protected String resolveParamValue(MatchingStrategy matchingStrategy) {
return matchingStrategy.serverValue.toString()
}
protected void processBodyElement(BlockBuilder blockBuilder, String property, Object value) {
}
protected void appendJsonPath(BlockBuilder blockBuilder, String json) {
blockBuilder.addLine("DocumentContext parsedJson = JsonPath.parse($json)")
}
protected void processBodyElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) {
blockBuilder.addLine("${exec.insertValue("parsedJson.read('\\\$$property')")}")
}
protected void processBodyElement(BlockBuilder blockBuilder, String property, Map.Entry entry) {
processBodyElement(blockBuilder, property + "." + entry.key, entry.value)
}
protected void processBodyElement(BlockBuilder blockBuilder, String property, Map map) {
map.each {
processBodyElement(blockBuilder, property, it)
}
}
protected void processBodyElement(BlockBuilder blockBuilder, String property, List list) {
list.eachWithIndex { listElement, listIndex ->
String prop = "$property[$listIndex]" ?: ''
processBodyElement(blockBuilder, prop, listElement)
}
}
protected ContentType getRequestContentType() {
ContentType contentType = recognizeContentTypeFromHeader(request.headers)
if (contentType == ContentType.UNKNOWN) {
contentType = recognizeContentTypeFromContent(request.body.serverValue)
}
return contentType
}
protected ContentType getResponseContentType() {
ContentType contentType = recognizeContentTypeFromHeader(response.headers)
if (contentType == ContentType.UNKNOWN) {
contentType = recognizeContentTypeFromContent(response.body.serverValue)
}
return contentType
}
protected String getTestSideValue(Object object) {
return MapConverter.getClientOrServerSideValues(object, TEST_SIDE).toString()
}
}

View File

@@ -23,7 +23,7 @@ class AccurestConfigProperties {
File generatedTestSourcesDir
/**
* Dir where the generated Wiremock stubs from Groovy DSL should be placed.
* Dir where the generated WireMock stubs from Groovy DSL should be placed.
* You can then mention them in your packaging task to create jar with stubs
*/
File stubsOutputDir

View File

@@ -4,5 +4,5 @@ package io.codearte.accurest.config
* @author Jakub Kubrynski
*/
enum TestMode {
MOCKMVC, EXPLICIT
MOCKMVC, EXPLICIT, JAXRSCLIENT
}

View File

@@ -0,0 +1,103 @@
package io.codearte.accurest.dsl
import groovy.json.JsonBuilder
import groovy.transform.TypeChecked
import io.codearte.accurest.dsl.internal.DslProperty
import io.codearte.accurest.dsl.internal.Header
import io.codearte.accurest.dsl.internal.Headers
import io.codearte.accurest.util.ContentType
import io.codearte.accurest.util.ContentUtils
import io.codearte.accurest.util.MapConverter
import java.util.regex.Pattern
import static io.codearte.accurest.util.ContentUtils.extractValue
import static io.codearte.accurest.util.MapConverter.transformValues
@TypeChecked
abstract class BaseWireMockStubStrategy {
private static final Boolean STUB_SIDE = true
protected getStubSideValue(Object object) {
return MapConverter.getClientOrServerSideValues(object, STUB_SIDE)
}
private static Closure transform = {
it instanceof DslProperty ? transformValues(it.clientValue, transform) : it
}
protected Map buildClientRequestHeadersSection(Headers headers) {
if (!headers) {
return null
}
return headers.entries.collectEntries { Header entry ->
parseHeader(entry.name, entry.clientValue)
}
}
protected Map buildClientResponseHeadersSection(Headers headers) {
if (!headers) {
return null
}
return headers.entries.collectEntries { Header entry ->
[(entry.name): entry.clientValue]
}
}
protected Map parseHeader(String entryKey, Object entry) {
return [(entryKey): [equalTo: entry]]
}
protected Map parseHeader(String entryKey, String entry) {
return [(entryKey): [equalTo: entry]]
}
protected Map parseHeader(String entryKey, Pattern entry) {
return [(entryKey): [matches: entry.pattern()]]
}
public String parseBody(Object value, ContentType contentType) {
return parseBody(value.toString(), contentType)
}
public Boolean parseBody(Boolean value, ContentType contentType) {
return value
}
public String parseBody(Map map, ContentType contentType) {
def transformedMap = MapConverter.getClientOrServerSideValues(map, true)
return parseBody(toJson(transformedMap), contentType)
}
public String parseBody(List list, ContentType contentType) {
return parseBody(toJson(list), contentType)
}
public String parseBody(GString value, ContentType contentType) {
Object processedValue = extractValue(value, contentType, { DslProperty dslProperty -> dslProperty.clientValue })
if (processedValue instanceof GString) {
return parseBody(processedValue.toString(), contentType)
}
return parseBody(processedValue, contentType)
}
public String parseBody(String value, ContentType contentType) {
return value
}
private static toJson(Object value) {
return new JsonBuilder(value).toString()
}
protected ContentType tryToGetContentType(Object body, Headers headers) {
ContentType contentType = ContentUtils.recognizeContentTypeFromHeader(headers)
if (contentType == ContentType.UNKNOWN) {
if (!body) {
return ContentType.UNKNOWN
}
return ContentUtils.getClientContentType(body)
}
return contentType
}
}

View File

@@ -1,67 +0,0 @@
package io.codearte.accurest.dsl
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.transform.TypeChecked
import groovy.xml.XmlUtil
import io.codearte.accurest.dsl.internal.Header
import io.codearte.accurest.dsl.internal.Headers
import java.util.regex.Pattern
import static groovy.json.StringEscapeUtils.escapeJava
@TypeChecked
abstract class BaseWiremockStubStrategy {
protected Map buildClientRequestHeadersSection(Headers headers) {
if (!headers) {
return null
}
return headers.entries.collectEntries { Header entry ->
parseHeader(entry.name, entry.clientValue)
}
}
protected Map buildClientResponseHeadersSection(Headers headers) {
if (!headers) {
return null
}
return headers.entries.collectEntries { Header entry ->
[(entry.name) : entry.clientValue]
}
}
protected Map parseHeader(String entryKey, Object entry) {
return [(entryKey): [equalTo : entry]]
}
protected Map parseHeader(String entryKey, String entry) {
return [(entryKey): [equalTo : entry]]
}
protected Map parseHeader(String entryKey, Pattern entry) {
return [(entryKey): [matches : entry.pattern()]]
}
protected String parseBody(Object body) {
String bodyAsString = body as String
try {
def json = new JsonSlurper().parseText(bodyAsString)
return escapeJava(JsonOutput.toJson(bodyAsString))
} catch (Exception jsonException) {
try {
def xml = new XmlSlurper().parseText(bodyAsString)
return escapeJava(XmlUtil.serialize(bodyAsString))
} catch (Exception xmlException) {
return escapeJava(bodyAsString)
}
}
}
protected String parseBody(List body) {
return JsonOutput.toJson(body)
}
protected String parseBody(Map body) {
return JsonOutput.toJson(body)
}
}

View File

@@ -7,10 +7,11 @@ import io.codearte.accurest.dsl.internal.Request
import io.codearte.accurest.dsl.internal.Response
@TypeChecked
@EqualsAndHashCode(includeFields = true)
@EqualsAndHashCode
@ToString(includeFields = true, includePackage = false, includeNames = true)
class GroovyDsl {
Integer priority
Request request
Response response
@@ -21,6 +22,10 @@ class GroovyDsl {
return dsl
}
void priority(int priority) {
this.priority = priority
}
void request(@DelegatesTo(Request) Closure closure) {
this.request = new Request()
closure.delegate = request

View File

@@ -0,0 +1,232 @@
package io.codearte.accurest.dsl
import com.github.tomakehurst.wiremock.http.RequestMethod
import com.github.tomakehurst.wiremock.matching.RequestPattern
import com.github.tomakehurst.wiremock.matching.ValuePattern
import groovy.json.JsonOutput
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import groovy.transform.TypeCheckingMode
import io.codearte.accurest.dsl.internal.*
import io.codearte.accurest.util.ContentType
import io.codearte.accurest.util.ContentUtils
import io.codearte.accurest.util.JsonToJsonPathsConverter
import io.codearte.accurest.util.JsonPaths
import io.codearte.accurest.util.MapConverter
import java.util.regex.Pattern
import static io.codearte.accurest.util.ContentUtils.*
import static io.codearte.accurest.util.RegexpBuilders.buildGStringRegexpForStubSide
import static io.codearte.accurest.util.RegexpBuilders.buildJSONRegexpMatch
@TypeChecked
@PackageScope
class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
private final Request request
WireMockRequestStubStrategy(GroovyDsl groovyDsl) {
this.request = groovyDsl.request
}
@PackageScope
RequestPattern buildClientRequestContent() {
RequestPattern requestPattern = new RequestPattern()
appendMethod(requestPattern)
appendHeaders(requestPattern)
appendUrl(requestPattern)
appendQueryParameters(requestPattern)
appendBody(requestPattern)
return requestPattern
}
private void appendMethod(RequestPattern requestPattern) {
if(!request.method) {
return
}
requestPattern.setMethod(RequestMethod.fromString(request.method.clientValue?.toString()))
}
private void appendBody(RequestPattern requestPattern) {
if (!request.body) {
return
}
ContentType contentType = tryToGetContentType(request.body.clientValue, request.headers)
if (contentType == ContentType.JSON) {
JsonPaths values = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValues(getMatchingStrategyFromBody(request.body)?.clientValue)
if (values.empty) {
requestPattern.bodyPatterns = [new ValuePattern(jsonCompareMode: org.skyscreamer.jsonassert.JSONCompareMode.LENIENT,
equalToJson: JsonOutput.toJson(getMatchingStrategy(request.body.clientValue).clientValue) ) ]
} else {
requestPattern.bodyPatterns = values.collect { new ValuePattern(matchesJsonPath: it.jsonPath) } ?: null
}
} else if (contentType == ContentType.XML) {
requestPattern.bodyPatterns = [new ValuePattern(equalToXml: getMatchingStrategy(request.body.clientValue).clientValue.toString())]
} else if (containsPattern(request?.body)) {
MatchingStrategy matchingStrategy = appendBodyRegexpMatchPattern(request.body)
requestPattern.bodyPatterns = [convertToValuePattern(matchingStrategy)]
} else {
requestPattern.bodyPatterns = [convertToValuePattern(getMatchingStrategy(request.body.clientValue))]
}
}
private void appendHeaders(RequestPattern requestPattern) {
if(!request.headers) {
return
}
request.headers.entries.each {
requestPattern.addHeader(it.name, convertToValuePattern(it.clientValue))
}
}
private void appendUrl(RequestPattern requestPattern) {
Object urlPath = request?.urlPath?.clientValue
if (urlPath) {
requestPattern.setUrlPath(getStubSideValue(urlPath.toString()).toString())
}
if(!request.url) {
return
}
Object url = getUrlIfGstring(request?.url?.clientValue)
if(url instanceof Pattern) {
requestPattern.setUrlPattern(url.pattern())
} else {
requestPattern.setUrl(url.toString())
}
}
private Object getUrlIfGstring(Object clientSide) {
if (clientSide instanceof GString) {
if (clientSide.values.any { getStubSideValue(it) instanceof Pattern }) {
return Pattern.compile(getStubSideValue(clientSide).toString())
} else {
return getStubSideValue(clientSide).toString()
}
}
return clientSide
}
private void appendQueryParameters(RequestPattern requestPattern) {
QueryParameters queryParameters = request?.urlPath?.queryParameters ?: request?.url?.queryParameters
queryParameters?.parameters?.each {
requestPattern.addQueryParam(it.name, convertToValuePattern(it.clientValue))
}
}
@TypeChecked(TypeCheckingMode.SKIP)
private static ValuePattern convertToValuePattern(Object object) {
switch (object) {
case Pattern:
Pattern value = object as Pattern
return ValuePattern.matches(value.pattern())
case MatchingStrategy:
MatchingStrategy value = object as MatchingStrategy
switch (value.type) {
case MatchingStrategy.Type.NOT_MATCHING:
return new ValuePattern(doesNotMatch: value.clientValue)
case MatchingStrategy.Type.ABSENT:
return ValuePattern.absent()
default:
return ValuePattern."${value.type.name}"(value.clientValue)
}
default:
return ValuePattern.equalTo(object.toString())
}
}
private MatchingStrategy getMatchingStrategyFromBody(Body body) {
if(!body) {
return null
}
return getMatchingStrategy(body.clientValue)
}
private MatchingStrategy getMatchingStrategy(MatchingStrategy matchingStrategy) {
return getMatchingStrategyIncludingContentType(matchingStrategy)
}
private MatchingStrategy getMatchingStrategy(GString gString) {
if (!gString) {
return new MatchingStrategy("", MatchingStrategy.Type.EQUAL_TO)
}
def extractedValue = ContentUtils.extractValue(gString) {
it instanceof DslProperty ? it.clientValue : getStringFromGString(it)
}
def value = getStringFromGString(extractedValue)
return getMatchingStrategy(value)
}
private def getStringFromGString(Object object) {
return object instanceof GString ? object.toString() : object
}
private MatchingStrategy getMatchingStrategy(Object bodyValue) {
return tryToFindMachingStrategy(bodyValue)
}
private MatchingStrategy tryToFindMachingStrategy(Object bodyValue) {
return new MatchingStrategy(MapConverter.transformToClientValues(bodyValue), getEqualsTypeFromContentTypeHeader())
}
private MatchingStrategy getMatchingStrategyIncludingContentType(MatchingStrategy matchingStrategy) {
MatchingStrategy.Type type = matchingStrategy.type
Object value = matchingStrategy.clientValue
ContentType contentType = recognizeContentTypeFromMatchingStrategy(type)
if (contentType == ContentType.UNKNOWN && type == MatchingStrategy.Type.EQUAL_TO) {
contentType = recognizeContentTypeFromContent(value)
type = getEqualsTypeFromContentType(contentType)
}
return new MatchingStrategy(parseBody(value, contentType), type)
}
private MatchingStrategy appendBodyRegexpMatchPattern(Object value, ContentType contentType) {
switch (contentType) {
case ContentType.JSON:
return new MatchingStrategy(buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING)
case ContentType.UNKNOWN:
return new MatchingStrategy(buildGStringRegexpForStubSide(value), MatchingStrategy.Type.MATCHING)
case ContentType.XML:
throw new IllegalStateException("XML pattern matching is not implemented yet")
}
}
private MatchingStrategy appendBodyRegexpMatchPattern(Object value) {
return appendBodyRegexpMatchPattern(value, ContentType.UNKNOWN)
}
private boolean containsPattern(GString bodyAsValue) {
return containsPattern(bodyAsValue.values)
}
private boolean containsPattern(Map map) {
return containsPattern(map.entrySet())
}
private boolean containsPattern(Collection collection) {
return collection.collect(this.&containsPattern).inject('') { a, b -> a || b }
}
private boolean containsPattern(Object[] objects) {
return containsPattern(objects.toList())
}
private boolean containsPattern(Map.Entry entry) {
return containsPattern(entry.value)
}
private boolean containsPattern(DslProperty dslProperty) {
return containsPattern(dslProperty.clientValue)
}
private boolean containsPattern(Pattern pattern) {
return true
}
private boolean containsPattern(Object o) {
return false
}
private MatchingStrategy.Type getEqualsTypeFromContentTypeHeader() {
return getEqualsTypeFromContentType(recognizeContentTypeFromHeader(request.headers))
}
}

View File

@@ -0,0 +1,57 @@
package io.codearte.accurest.dsl
import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder
import com.github.tomakehurst.wiremock.http.HttpHeader
import com.github.tomakehurst.wiremock.http.HttpHeaders
import com.github.tomakehurst.wiremock.http.ResponseDefinition
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import io.codearte.accurest.dsl.internal.Request
import io.codearte.accurest.dsl.internal.Response
import io.codearte.accurest.util.ContentType
import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromContent
import static io.codearte.accurest.util.ContentUtils.recognizeContentTypeFromHeader
@TypeChecked
@PackageScope
class WireMockResponseStubStrategy extends BaseWireMockStubStrategy {
private final Request request
private final Response response
WireMockResponseStubStrategy(GroovyDsl groovyDsl) {
this.response = groovyDsl.response
this.request = groovyDsl.request
}
@PackageScope
ResponseDefinition buildClientResponseContent() {
ResponseDefinitionBuilder builder = new ResponseDefinitionBuilder()
.withStatus(response.status.clientValue as Integer)
appendHeaders(builder)
appendBody(builder)
return builder.build()
}
private void appendHeaders(ResponseDefinitionBuilder builder) {
if (response.headers) {
builder.withHeaders(new HttpHeaders(response.headers.entries?.collect {
new HttpHeader(it.name, it.clientValue.toString())
}))
}
}
private void appendBody(ResponseDefinitionBuilder builder) {
if (response.body) {
Object body = response.body.clientValue
ContentType contentType = recognizeContentTypeFromHeader(response.headers)
if (contentType == ContentType.UNKNOWN) {
contentType = recognizeContentTypeFromContent(body)
}
builder.withBody(parseBody(body, contentType))
}
}
}

View File

@@ -0,0 +1,34 @@
package io.codearte.accurest.dsl
import com.github.tomakehurst.wiremock.http.ResponseDefinition
import com.github.tomakehurst.wiremock.matching.RequestPattern
import com.github.tomakehurst.wiremock.stubbing.StubMapping
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
@CompileStatic
class WireMockStubStrategy {
private final WireMockRequestStubStrategy wireMockRequestStubStrategy
private final WireMockResponseStubStrategy wireMockResponseStubStrategy
private final Integer priority
WireMockStubStrategy(GroovyDsl groovyDsl) {
this.wireMockRequestStubStrategy = new WireMockRequestStubStrategy(groovyDsl)
this.wireMockResponseStubStrategy = new WireMockResponseStubStrategy(groovyDsl)
this.priority = groovyDsl.priority
}
@CompileDynamic
String toWireMockClientStub() {
StubMapping stubMapping = new StubMapping()
RequestPattern request = wireMockRequestStubStrategy.buildClientRequestContent()
ResponseDefinition response = wireMockResponseStubStrategy.buildClientResponseContent()
if (priority) {
stubMapping.priority = priority
}
stubMapping.request = request
stubMapping.response = response
return StubMapping.buildJsonStringFor(stubMapping)
}
}

View File

@@ -1,52 +0,0 @@
package io.codearte.accurest.dsl
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import io.codearte.accurest.dsl.internal.ClientRequest
import io.codearte.accurest.dsl.internal.Request
import java.util.regex.Pattern
@TypeChecked
@PackageScope
class WiremockRequestStubStrategy extends BaseWiremockStubStrategy {
private final Request request
WiremockRequestStubStrategy(GroovyDsl groovyDsl) {
this.request = groovyDsl.request
}
@PackageScope
Map buildClientRequestContent() {
return buildRequestContent(new ClientRequest(request))
}
private Map<String, Object> buildRequestContent(ClientRequest request) {
return ([method : request?.method?.clientValue,
headers : buildClientRequestHeadersSection(request.headers)
] << appendUrl(request) << appendBody(request)).findAll { it.value }
}
private Map<String, Object> appendUrl(ClientRequest clientRequest) {
Object url = clientRequest?.url?.clientValue
return url instanceof Pattern ? [urlPattern: ((Pattern)url).pattern()] : [url: url]
}
private Map<String, Object> appendBody(ClientRequest clientRequest) {
Object body = clientRequest?.body?.clientValue
if (body == null) {
return [:]
}
if (containsRegex(body)) {
return [bodyPatterns: [[matches: parseBody(body)]]]
}
return [bodyPatterns: [[equalTo: parseBody(body)]]]
}
boolean containsRegex(Object bodyObject) {
String bodyString = bodyObject as String
return (bodyString =~ /\^.*\$/).find()
}
}

View File

@@ -1,32 +0,0 @@
package io.codearte.accurest.dsl
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import io.codearte.accurest.dsl.internal.ClientResponse
import io.codearte.accurest.dsl.internal.Response
@TypeChecked
@PackageScope
class WiremockResponseStubStrategy extends BaseWiremockStubStrategy {
private final Response response
WiremockResponseStubStrategy(GroovyDsl groovyDsl) {
this.response = groovyDsl.response
}
@PackageScope
Map buildClientResponseContent() {
return buildResponseContent(new ClientResponse(response))
}
private Map<String, Object> buildResponseContent(ClientResponse response) {
return ([status : response?.status?.clientValue,
headers: buildClientResponseHeadersSection(response.headers)
] << appendBody(response)).findAll { it.value }
}
private Map<String, Object> appendBody(ClientResponse response) {
Object body = response?.body?.clientValue
return body != null ? [body: parseBody(body)] : [:]
}
}

View File

@@ -1,21 +0,0 @@
package io.codearte.accurest.dsl
import groovy.json.JsonOutput
import groovy.transform.CompileStatic
@CompileStatic
class WiremockStubStrategy {
private final WiremockRequestStubStrategy wiremockRequestStubStrategy
private final WiremockResponseStubStrategy wiremockResponseStubStrategy
WiremockStubStrategy(GroovyDsl groovyDsl) {
this.wiremockRequestStubStrategy = new WiremockRequestStubStrategy(groovyDsl)
this.wiremockResponseStubStrategy = new WiremockResponseStubStrategy(groovyDsl)
}
String toWiremockClientStub() {
return JsonOutput.prettyPrint(JsonOutput.toJson([request : wiremockRequestStubStrategy.buildClientRequestContent(),
response: wiremockResponseStubStrategy.buildClientResponseContent()]))
}
}

View File

@@ -1,16 +1,16 @@
package io.codearte.accurest.dsl.internal
import groovy.json.JsonSlurper
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import org.codehaus.groovy.runtime.GStringImpl
@ToString(includePackage = false, includeFields = true, includeNames = true)
@EqualsAndHashCode(includeFields = true)
@CompileStatic
class Body extends DslProperty {
Body(Map<String, DslProperty> body) {
super(extractValue(body, {it.clientValue}), extractValue(body, {it.serverValue}))
super(extractValue(body, { DslProperty p -> p.clientValue}), extractValue(body, {DslProperty p -> p.serverValue}))
}
private static Map<String, Object> extractValue(Map<String, DslProperty> body, Closure valueProvider) {
@@ -19,8 +19,8 @@ class Body extends DslProperty {
} as Map<String, Object>
}
Body(List bodyAsList) {
super(bodyAsList.collect { it.clientValue }, bodyAsList.collect { it.serverValue })
Body(List<DslProperty> bodyAsList) {
super(bodyAsList.collect { DslProperty p -> p.clientValue }, bodyAsList.collect { DslProperty p -> p.serverValue })
}
Body(Object bodyAsValue) {
@@ -28,17 +28,16 @@ class Body extends DslProperty {
}
Body(GString bodyAsValue) {
super(extractValue(bodyAsValue, {it.clientValue}), extractValue(bodyAsValue, {it.serverValue}))
super(bodyAsValue, bodyAsValue)
}
Body(DslProperty bodyAsValue) {
super(bodyAsValue.clientValue, bodyAsValue.serverValue)
}
private static Object extractValue(GString bodyAsValue, Closure valueProvider) {
GString clientGString = new GStringImpl(bodyAsValue.values.clone(), bodyAsValue.strings.clone())
Object[] clientValues = bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as Object[]
return new JsonSlurper().parseText(new GStringImpl(clientValues, clientGString.strings).toString())
Body(MatchingStrategy matchingStrategy) {
super(matchingStrategy, matchingStrategy)
}
}

View File

@@ -13,6 +13,8 @@ import java.util.regex.Pattern
@PackageScope
class Common {
@Delegate private final RegexPatterns regexPatterns = new RegexPatterns()
Map<String, DslProperty> convertObjectsToDslProperties(Map<String, Object> body) {
return body.collectEntries {
Map.Entry<String, Object> entry ->
@@ -20,10 +22,10 @@ class Common {
} as Map<String, DslProperty>
}
List convertObjectsToDslProperties(List body) {
return body.collect {
Collection convertObjectsToDslProperties(List body) {
return (body.collect {
Object element -> toDslProperty(element)
} as List
} as List)
}
DslProperty toDslProperty(Object property) {
@@ -47,10 +49,16 @@ class Common {
}
DslProperty value(ClientDslProperty client, ServerDslProperty server) {
assertThatSidesMatch(client.clientValue, server.serverValue)
return new DslProperty(client.clientValue, server.serverValue)
}
DslProperty value(Object value) {
return new DslProperty(value)
}
DslProperty value(ServerDslProperty server, ClientDslProperty client) {
assertThatSidesMatch(client.clientValue, server.serverValue)
return new DslProperty(client.clientValue, server.serverValue)
}
@@ -66,6 +74,10 @@ class Common {
return Pattern.compile(regex)
}
OptionalProperty optional(Object object) {
return new OptionalProperty(object)
}
ExecutionProperty execute(String commandToExecute) {
return new ExecutionProperty(commandToExecute)
}
@@ -74,7 +86,53 @@ class Common {
return new ClientDslProperty(clientValue)
}
ClientDslProperty stub(Object clientValue) {
return new ClientDslProperty(clientValue)
}
ServerDslProperty server(Object serverValue) {
return new ServerDslProperty(serverValue)
}
ServerDslProperty test(Object serverValue) {
return new ServerDslProperty(serverValue)
}
void assertThatSidesMatch(OptionalProperty stubSide, Object testSide) {
assert testSide ==~ Pattern.compile(stubSide.optionalPattern())
}
void assertThatSidesMatch(Pattern pattern, String value) {
assert value ==~ pattern
}
void assertThatSidesMatch(String value, Pattern pattern) {
assert value ==~ pattern
}
void assertThatSidesMatch(MatchingStrategy firstSide, MatchingStrategy secondSide) {
if (firstSide.type == MatchingStrategy.Type.ABSENT && secondSide != MatchingStrategy.Type.ABSENT) {
throwAbsentError()
}
}
void assertThatSidesMatch(MatchingStrategy firstSide, Object secondSide) {
if (firstSide.type == MatchingStrategy.Type.ABSENT) {
throwAbsentError()
}
}
void assertThatSidesMatch(Object firstSide, MatchingStrategy secondSide) {
if (secondSide.type == MatchingStrategy.Type.ABSENT) {
throwAbsentError()
}
}
private void throwAbsentError() {
throw new IllegalStateException("Absent cannot only be used only on one side")
}
void assertThatSidesMatch(Object firstSide, Object secondSide) {
// do nothing
}
}

View File

@@ -5,15 +5,15 @@ import groovy.transform.CompileStatic
@CompileStatic
class ExecutionProperty {
private static final String PLACEHOLDER_VALUE = '\\$it'
private static final String PLACEHOLDER_VALUE = '\\$it'
final String executionCommand
final String executionCommand
ExecutionProperty(String executionCommand) {
this.executionCommand = executionCommand
}
ExecutionProperty(String executionCommand) {
this.executionCommand = executionCommand
}
String insertValue(String valueToInsert) {
return executionCommand.replaceAll(PLACEHOLDER_VALUE, valueToInsert)
}
String insertValue(String valueToInsert) {
return executionCommand.replaceAll(PLACEHOLDER_VALUE, valueToInsert)
}
}

View File

@@ -8,16 +8,16 @@ import groovy.transform.ToString
@CompileStatic
class Header extends DslProperty {
String name
String name
Header(String name, DslProperty dslProperty) {
super(dslProperty.clientValue, dslProperty.serverValue)
this.name = name
}
Header(String name, DslProperty dslProperty) {
super(dslProperty.clientValue, dslProperty.serverValue)
this.name = name
}
Header(String name, Object value) {
super(value)
this.name = name
}
Header(String name, Object value) {
super(value)
this.name = name
}
}

View File

@@ -0,0 +1,32 @@
package io.codearte.accurest.dsl.internal
import groovy.json.JsonOutput
import groovy.transform.CompileStatic
import io.codearte.accurest.util.MapConverter
import java.util.regex.Pattern
@CompileStatic
class JsonStructureConverter {
public static final String TEMPORARY_PLACEHOLDER = '###PLACEHOLDER###'
public static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile(TEMPORARY_PLACEHOLDER)
static Object convertJsonStructureToObjectUnderstandingStructure(Object parsedJson,
Closure<Boolean> retrievePlaceholders,
Closure<String> performAdditionalLogicOnSerializedJson,
Closure convertSerializedJsonToSth) {
LinkedList<Object> queue = new LinkedList<>()
def transformedJson = MapConverter.transformValues(parsedJson, {
if(retrievePlaceholders(it)) {
queue.push(it)
return TEMPORARY_PLACEHOLDER
}
return it
})
String jsonAsString = JsonOutput.toJson(transformedJson)
String transformedJsonAsString = performAdditionalLogicOnSerializedJson(jsonAsString)
return convertSerializedJsonToSth(queue, transformedJsonAsString)
}
}

View File

@@ -0,0 +1,47 @@
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString;
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true)
@CompileStatic
class MatchingStrategy extends DslProperty {
Type type
JSONCompareMode jsonCompareMode
MatchingStrategy(Object value, Type type) {
this(value, type, null)
}
MatchingStrategy(Object value, Type type, JSONCompareMode jsonCompareMode) {
super(value)
this.type = type
this.jsonCompareMode = jsonCompareMode
}
MatchingStrategy(DslProperty value, Type type) {
this(value, type, null)
}
MatchingStrategy(DslProperty value, Type type, JSONCompareMode jsonCompareMode) {
super(value.clientValue, value.serverValue)
this.type = type
this.jsonCompareMode = jsonCompareMode
}
enum Type {
EQUAL_TO("equalTo"), CONTAINS("containing"), MATCHING("matches"), NOT_MATCHING("doesNotMatch"),
EQUAL_TO_JSON("equalToJson"), EQUAL_TO_XML("equalToXml"), ABSENT("absent")
final String name
Type(name) {
this.name = name
}
}
}

View File

@@ -0,0 +1,13 @@
package io.codearte.accurest.dsl.internal
class OptionalProperty {
final Object value
OptionalProperty(Object value) {
this.value = value
}
String optionalPattern() {
return "($value)?"
}
}

View File

@@ -0,0 +1,34 @@
package io.codearte.accurest.dsl.internal;
import groovy.transform.CompileStatic;
import groovy.transform.EqualsAndHashCode;
import groovy.transform.ToString
import static io.codearte.accurest.util.ValidateUtils.validateServerValueIsAvailable;
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true)
@CompileStatic
class QueryParameter extends DslProperty {
String name
QueryParameter(String name, DslProperty dslProperty) {
super(dslProperty.clientValue, dslProperty.serverValue)
validateServerValueIsAvailable(dslProperty.serverValue, "Query parameter '$name'")
this.name = name
}
QueryParameter(String name, MatchingStrategy matchingStrategy) {
super(matchingStrategy)
validateServerValueIsAvailable(matchingStrategy, "Query parameter '$name'")
this.name = name
}
QueryParameter(String name, Object value) {
super(value)
validateServerValueIsAvailable(value, "Query parameter '$name'")
this.name = name
}
}

View File

@@ -0,0 +1,23 @@
package io.codearte.accurest.dsl.internal
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.transform.TypeChecked
@EqualsAndHashCode(includeFields = true)
@ToString(includePackage = false, includeFields = true, ignoreNulls = true, includeNames = true)
@TypeChecked
class QueryParameters {
List<QueryParameter> parameters = []
void parameter(Map<String, Object> singleParameter) {
Map.Entry<String, Object> first = singleParameter.entrySet().first()
parameters << new QueryParameter(first?.key, first?.value)
}
void parameter(String parameterName, Object parameterValue) {
parameters << new QueryParameter(parameterName, parameterValue)
}
}

View File

@@ -0,0 +1,46 @@
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic
import java.util.regex.Pattern
@CompileStatic
class RegexPatterns {
private static final Pattern TRUE_OR_FALSE = Pattern.compile(/(true|false)/)
private static final Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/)
private static final Pattern NUMBER = Pattern.compile('-?\\d*(\\.\\d+)?')
private static final Pattern IP_ADDRESS = Pattern.compile('([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])');
private static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?');
private static final Pattern EMAIL = Pattern.compile('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}');
private static final Pattern URL = Pattern.compile('((www\\.|(http|https|ftp|news|file)+\\:\\/\\/)[_.a-z0-9-]+\\.[a-z0-9\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])')
String onlyAlphaUnicode() {
return ONLY_ALPHA_UNICODE.pattern()
}
String number() {
return NUMBER.pattern()
}
String anyBoolean() {
return TRUE_OR_FALSE.pattern()
}
String ipAddress() {
return IP_ADDRESS.pattern()
}
String hostname() {
return HOSTNAME_PATTERN.pattern()
}
String email() {
return EMAIL.pattern()
}
String url() {
return URL.pattern()
}
}

View File

@@ -5,12 +5,13 @@ import groovy.transform.ToString
import groovy.transform.TypeChecked
@TypeChecked
@EqualsAndHashCode(includeFields = true)
@EqualsAndHashCode
@ToString(includePackage = false, includeNames = true)
class Request extends Common {
DslProperty method
Url url
UrlPath urlPath
Headers headers
Body body
@@ -20,6 +21,7 @@ class Request extends Common {
Request(Request request) {
this.method = request.method
this.url = request.url
this.urlPath = request.urlPath
this.headers = request.headers
this.body = request.body
}
@@ -32,7 +34,7 @@ class Request extends Common {
this.method = toDslProperty(method)
}
void url(String url) {
void url(Object url) {
this.url = new Url(url)
}
@@ -40,6 +42,38 @@ class Request extends Common {
this.url = new Url(url)
}
void url(Object url, @DelegatesTo(UrlPath) Closure closure) {
this.url = new Url(url)
closure.delegate = this.url
closure()
}
void url(DslProperty url, @DelegatesTo(UrlPath) Closure closure) {
this.url = new Url(url)
closure.delegate = this.url
closure()
}
void urlPath(String path) {
this.urlPath = new UrlPath(path)
}
void urlPath(DslProperty path) {
this.urlPath = new UrlPath(path)
}
void urlPath(String path, @DelegatesTo(UrlPath) Closure closure) {
this.urlPath = new UrlPath(path)
closure.delegate = urlPath
closure()
}
void urlPath(DslProperty path, @DelegatesTo(UrlPath) Closure closure) {
this.urlPath = new UrlPath(path)
closure.delegate = urlPath
closure()
}
void headers(@DelegatesTo(Headers) Closure closure) {
this.headers = new Headers()
closure.delegate = headers
@@ -54,6 +88,10 @@ class Request extends Common {
this.body = new Body(convertObjectsToDslProperties(body))
}
void body(DslProperty dslProperty) {
this.body = new Body(dslProperty)
}
void body(Object bodyAsValue) {
this.body = new Body(bodyAsValue)
}
@@ -61,10 +99,43 @@ class Request extends Common {
Body getBody() {
return body
}
MatchingStrategy equalTo(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO)
}
MatchingStrategy containing(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.CONTAINS)
}
MatchingStrategy matching(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.MATCHING)
}
MatchingStrategy notMatching(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.NOT_MATCHING)
}
MatchingStrategy equalToXml(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_XML)
}
MatchingStrategy equalToJson(Object value) {
return new MatchingStrategy(value, MatchingStrategy.Type.EQUAL_TO_JSON)
}
MatchingStrategy absent() {
return new MatchingStrategy(true, MatchingStrategy.Type.ABSENT)
}
void assertThatSidesMatch(Object stubSide, OptionalProperty testSide) {
throw new IllegalStateException("Optional can be used only for the stub side of the request!")
}
}
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@EqualsAndHashCode
@ToString(includePackage = false)
class ServerRequest extends Request {
ServerRequest(Request request) {
@@ -73,7 +144,7 @@ class ServerRequest extends Request {
}
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@EqualsAndHashCode
@ToString(includePackage = false)
class ClientRequest extends Request {
ClientRequest(Request request) {

View File

@@ -6,13 +6,13 @@ import groovy.transform.ToString
import groovy.transform.TypeChecked
@TypeChecked
@EqualsAndHashCode(includeFields = true)
@EqualsAndHashCode
@ToString(includePackage = false, includeFields = true)
class Response extends Common {
private DslProperty status
private Headers headers
private Body body
DslProperty status
Headers headers
Body body
Response() {
}
@@ -49,21 +49,13 @@ class Response extends Common {
this.body = new Body(bodyAsValue)
}
Body getBody() {
return body
}
DslProperty getStatus() {
return status
}
Headers getHeaders() {
return headers
void assertThatSidesMatch(OptionalProperty stubSide, Object testSide) {
throw new IllegalStateException("Optional can be used only in the test side of the response!")
}
}
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@EqualsAndHashCode
@ToString(includePackage = false)
class ServerResponse extends Response {
ServerResponse(Response request) {
@@ -72,7 +64,7 @@ class ServerResponse extends Response {
}
@CompileStatic
@EqualsAndHashCode(includeFields = true)
@EqualsAndHashCode
@ToString(includePackage = false)
class ClientResponse extends Response {
ClientResponse(Response request) {

View File

@@ -1,18 +1,32 @@
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import static io.codearte.accurest.util.ValidateUtils.validateServerValueIsAvailable
@ToString(includePackage = false, includeFields = true, includeNames = true)
@EqualsAndHashCode(includeFields = true)
@CompileStatic
class Url extends DslProperty {
Url(DslProperty bodyAsValue) {
super(bodyAsValue.clientValue, bodyAsValue.serverValue)
QueryParameters queryParameters
Url(DslProperty prop) {
super(prop.clientValue, prop.serverValue)
validateServerValueIsAvailable(prop.serverValue, "Url")
}
Url(String bodyAsValue) {
super(bodyAsValue)
Url(Object url) {
super(url)
validateServerValueIsAvailable(url, "Url")
}
void queryParameters(@DelegatesTo(QueryParameters) Closure closure) {
this.queryParameters = new QueryParameters()
closure.delegate = queryParameters
closure()
}
}

View File

@@ -0,0 +1,20 @@
package io.codearte.accurest.dsl.internal
import groovy.transform.CompileStatic;
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString;
@ToString(includePackage = false, includeFields = true, includeNames = true)
@EqualsAndHashCode(includeFields = true)
@CompileStatic
class UrlPath extends Url {
UrlPath(String path) {
super(path)
}
UrlPath(DslProperty path) {
super(path)
}
}

View File

@@ -0,0 +1,15 @@
package io.codearte.accurest.util
enum ContentType {
JSON("application/json"),
XML("application/xml"),
UNKNOWN("application/octet-stream")
final String mimeType
ContentType(String mimeType) {
this.mimeType = mimeType
}
}

View File

@@ -0,0 +1,323 @@
package io.codearte.accurest.util
import groovy.json.JsonException
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.transform.TypeChecked
import groovy.util.logging.Slf4j
import io.codearte.accurest.dsl.internal.DslProperty
import io.codearte.accurest.dsl.internal.ExecutionProperty
import io.codearte.accurest.dsl.internal.Headers
import io.codearte.accurest.dsl.internal.MatchingStrategy
import io.codearte.accurest.dsl.internal.OptionalProperty
import org.codehaus.groovy.runtime.GStringImpl
import java.util.regex.Matcher
import java.util.regex.Pattern
import static org.apache.commons.lang3.StringEscapeUtils.escapeJson
import static org.apache.commons.lang3.StringEscapeUtils.escapeXml11
@TypeChecked
@Slf4j
class ContentUtils {
public static final Closure GET_STUB_SIDE = {
it instanceof DslProperty ? it.clientValue : it
}
private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('.*REGEXP>>(.*)<<.*')
private static final Pattern TEMPORARY_EXECUTION_PATTERN_HOLDER = Pattern.compile('EXECUTION>>(.*)<<')
private static final Pattern TEMPORARY_OPTIONAL_PATTERN_HOLDER = Pattern.compile('OPTIONAL>>(.*)<<')
private static final String JSON_VALUE_PATTERN_FOR_REGEX = 'REGEXP>>%s<<'
private static final String JSON_VALUE_PATTERN_FOR_OPTIONAL = 'OPTIONAL>>%s<<'
private static final String JSON_VALUE_PATTERN_FOR_EXECUTION = '"EXECUTION>>%s<<"'
/**
* Due to the fact that we allow users to have a body with GString and different values inside
* we need to be prepared that they pass regexps around both on client and server side.
*
* In order to preserve the original JSON structure we need to convert the passed Regex patterns
* to a temporary string, then convert all to a legitimate JSON structure and then finally
* convert it back from string to a pattern.
*
* @param bodyAsValue - GString with passed values
* @param valueProvider - provider of values either for server or client side
* @return JSON structure with replaced client / server side parts
*/
public static Object extractValue(GString bodyAsValue, ContentType contentType, Closure valueProvider) {
if (bodyAsValue.isEmpty()){
return bodyAsValue
}
if (contentType == ContentType.JSON) {
return extractValueForJSON(bodyAsValue, valueProvider)
}
if (contentType == ContentType.XML) {
return extractValueForXML(bodyAsValue, valueProvider)
}
// else Brute force :(
try {
log.debug("No content type provided so trying to parse as JSON")
return extractValueForJSON(bodyAsValue, valueProvider)
} catch(JsonException e) {
// Not a JSON format
log.debug("Failed to parse as JSON - trying to parse as XML", e)
try {
return extractValueForXML(bodyAsValue, valueProvider)
} catch (Exception exception) {
log.debug("No content type provided and failed to parse as XML - returning the value back to the user", exception)
return extractValueForGString(bodyAsValue, valueProvider)
}
}
}
public static ContentType getClientContentType(GString bodyAsValue) {
try {
extractValueForJSON(bodyAsValue, GET_STUB_SIDE)
return ContentType.JSON
} catch(JsonException e) {
try {
new XmlSlurper().parseText(extractValueForXML(bodyAsValue, GET_STUB_SIDE).toString())
return ContentType.XML
} catch (Exception exception) {
extractValueForGString(bodyAsValue, GET_STUB_SIDE)
return ContentType.UNKNOWN
}
}
}
public static ContentType getClientContentType(String bodyAsValue) {
try {
new JsonSlurper().parseText(bodyAsValue)
return ContentType.JSON
} catch(JsonException e) {
try {
new XmlSlurper().parseText(bodyAsValue)
return ContentType.XML
} catch (Exception exception) {
return ContentType.UNKNOWN
}
}
}
public static ContentType getClientContentType(Object bodyAsValue) {
return ContentType.UNKNOWN
}
public static ContentType getClientContentType(Map bodyAsValue) {
try {
JsonOutput.toJson(bodyAsValue)
return ContentType.JSON
} catch (Exception ignore) {
return ContentType.UNKNOWN
}
}
public static ContentType getClientContentType(List bodyAsValue) {
try {
JsonOutput.toJson(bodyAsValue)
return ContentType.JSON
} catch (Exception ignore) {
return ContentType.UNKNOWN
}
}
private static GStringImpl extractValueForGString(GString bodyAsValue, Closure valueProvider) {
return new GStringImpl(
bodyAsValue.values.collect { it instanceof DslProperty ? valueProvider(it) : it } as String[],
bodyAsValue.strings.clone() as String[]
)
}
public static Object extractValue(GString bodyAsValue, Closure valueProvider) {
return extractValue(bodyAsValue, ContentType.UNKNOWN, valueProvider)
}
private static Object extractValueForJSON(GString bodyAsValue, Closure valueProvider) {
GString transformedString = new GStringImpl(
bodyAsValue.values.collect { transformJSONStringValue(it, valueProvider) } as String[],
bodyAsValue.strings.clone() as String[]
)
def parsedJson = new JsonSlurper().parseText(transformedString.toString().replace('\\', '\\\\'))
return convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson)
}
private static GStringImpl extractValueForXML(GString bodyAsValue, Closure valueProvider) {
return new GStringImpl(
bodyAsValue.values.collect { transformXMLStringValue(it, valueProvider) } as String[],
bodyAsValue.strings.clone() as String[]
)
}
private static String transformJSONStringValue(Object obj, Closure valueProvider) {
return obj.toString()
}
private static String transformJSONStringValue(DslProperty dslProperty, Closure valueProvider) {
return transformJSONStringValue(valueProvider(dslProperty), valueProvider)
}
private static String transformJSONStringValue(Pattern pattern, Closure valueProvider) {
return String.format(JSON_VALUE_PATTERN_FOR_REGEX, pattern.pattern())
}
private static String transformJSONStringValue(OptionalProperty optional, Closure valueProvider) {
return String.format(JSON_VALUE_PATTERN_FOR_OPTIONAL, optional.value)
}
private static String transformJSONStringValue(ExecutionProperty property, Closure valueProvider) {
return String.format(JSON_VALUE_PATTERN_FOR_EXECUTION, property.executionCommand)
}
private static String transformXMLStringValue(Object obj, Closure valueProvider) {
return escapeXml11(obj.toString())
}
private static String transformXMLStringValue(DslProperty dslProperty, Closure valueProvider) {
return transformXMLStringValue(valueProvider(dslProperty), valueProvider)
}
private static Object convertAllTemporaryRegexPlaceholdersBackToPatterns(parsedJson) {
MapConverter.transformValues(parsedJson, { Object value ->
if (value instanceof String) {
String string = (String) value
return returnParsedObject(string)
}
return value
})
}
/**
* <p>
* If you wonder why there is val[1] without null-check then take a look at this:
* </p>
* <p>
* Example:
* </p>
* <p>
* Our string equals: {@code EXECUTION>>assertThatRejectionReasonIsNull($it)<<}
* The matcher matches this group with the pattern {@code EXECUTION>>(.*)<<}
* </p>
* <p>
* So {@code executionMatcher[0]} returns 2 elements:
* <ul>
* <li> index0: EXECUTION>>assertThatRejectionReasonIsNull($it)<< </li>
* <li> index1: assertThatRejectionReasonIsNull($it)<< </li>
* </ul>
* </p>
* <p>
* Thus one can safely write {@code executionMatcher[0][1]} to retrieve the matched group
* </p>
* @param string to match the regexps against
* @return object converted from temporary holders
*/
static Object returnParsedObject(String string) {
Matcher matcher = TEMPORARY_PATTERN_HOLDER.matcher(string.trim())
if (matcher.matches()) {
return Pattern.compile(patternFromMatchingGroup(matcher))
}
Matcher executionMatcher = TEMPORARY_EXECUTION_PATTERN_HOLDER.matcher(string.trim())
if (executionMatcher.matches()) {
return new ExecutionProperty(patternFromMatchingGroup(executionMatcher))
}
Matcher optionalMatcher = TEMPORARY_OPTIONAL_PATTERN_HOLDER.matcher(string.trim())
if (optionalMatcher.matches()) {
String patternToMatch = patternFromMatchingGroup(optionalMatcher)
return Pattern.compile(new OptionalProperty(patternToMatch).optionalPattern())
}
return string
}
private static String patternFromMatchingGroup(Matcher matcher) {
List val = matcher[0] as List
return val[1]
}
public static ContentType recognizeContentTypeFromHeader(Headers headers) {
String content = headers?.entries.find { it.name == "Content-Type" } ?.clientValue?.toString()
if (content?.endsWith("json")) {
return ContentType.JSON
}
if (content?.endsWith("xml")) {
return ContentType.XML
}
return ContentType.UNKNOWN
}
public static MatchingStrategy.Type getEqualsTypeFromContentType(ContentType contentType) {
switch (contentType) {
case ContentType.JSON:
return MatchingStrategy.Type.EQUAL_TO_JSON
case ContentType.XML:
return MatchingStrategy.Type.EQUAL_TO_XML
}
return MatchingStrategy.Type.EQUAL_TO
}
public static ContentType recognizeContentTypeFromContent(GString gstring) {
if (isJsonType(gstring)) {
return ContentType.JSON
}
if (isXmlType(gstring)) {
return ContentType.XML
}
return ContentType.UNKNOWN
}
public static ContentType recognizeContentTypeFromContent(Map jsonMap) {
return ContentType.JSON
}
public static ContentType recognizeContentTypeFromContent(List jsonList) {
return ContentType.JSON
}
public static ContentType recognizeContentTypeFromContent(Object gstring) {
return ContentType.UNKNOWN
}
public static boolean isJsonType(GString gstring) {
if (gstring.isEmpty()) {
return false
}
GString stringWithoutValues = new GStringImpl(
gstring.values.collect({
it instanceof String || it instanceof GString ? it.toString() : escapeJson(it.toString())
}) as Object[],
gstring.strings.clone() as String[]
)
try {
new JsonSlurper().parseText(stringWithoutValues.toString())
return true
} catch (JsonException e) {
// Not JSON
}
return false
}
public static boolean isXmlType(GString gstring) {
GString stringWithoutValues = new GStringImpl(
gstring.values.collect({
it instanceof String || it instanceof GString ? it.toString() : escapeXml11(it.toString())
}) as Object[],
gstring.strings.clone() as String[]
)
try {
new XmlSlurper().parseText(stringWithoutValues.toString())
return true
} catch (Exception e) {
// Not XML
}
return false
}
public static ContentType recognizeContentTypeFromMatchingStrategy(MatchingStrategy.Type type) {
switch (type) {
case MatchingStrategy.Type.EQUAL_TO_XML:
return ContentType.XML
case MatchingStrategy.Type.EQUAL_TO_JSON:
return ContentType.JSON
}
return ContentType.UNKNOWN
}
}

View File

@@ -0,0 +1,40 @@
package io.codearte.accurest.util
import java.util.regex.Pattern
class JsonPathEntry {
final String jsonPath
final String optionalSuffix
final Object value
JsonPathEntry(String jsonPath, String optionalSuffix, Object value) {
this.jsonPath = jsonPath
this.optionalSuffix = optionalSuffix
this.value = value
}
List<String> buildJsonPathComparison(String parsedJsonVariable) {
if (optionalSuffix) {
return ["!${parsedJsonVariable}.read('''${jsonPath}''', JSONArray).empty"]
} else if (traversesOverCollections()) {
return ["${parsedJsonVariable}.read('''${jsonPath}''', JSONArray).get(0) ${operator()} ${potentiallyWrappedWithQuotesValue()}"]
}
return ["${parsedJsonVariable}.read('''${jsonPath}''') ${operator()} ${potentiallyWrappedWithQuotesValue()}"]
}
private boolean traversesOverCollections() {
return jsonPath.contains('[*]')
}
String operator() {
return value instanceof Pattern ? "==~" : "=="
}
String potentiallyWrappedWithQuotesValue() {
return value instanceof Number ? value : "'''$value'''"
}
static JsonPathEntry simple(String jsonPath, Object value) {
return new JsonPathEntry(jsonPath, "", value)
}
}

View File

@@ -0,0 +1,23 @@
package io.codearte.accurest.util
class JsonPaths extends HashSet<JsonPathEntry> {
Object getAt(String key) {
return find {
it.jsonPath == key
}?.value
}
Object putAt(String key, Object value) {
JsonPathEntry entry = find {
it.jsonPath == key
}
if (!entry) {
return null
}
Object oldValue = entry.value
add(new JsonPathEntry(entry.jsonPath, entry.optionalSuffix, value))
return oldValue
}
}

View File

@@ -0,0 +1,148 @@
package io.codearte.accurest.util
import java.util.regex.Pattern
import groovy.json.JsonSlurper
import io.codearte.accurest.dsl.internal.ExecutionProperty
import io.codearte.accurest.dsl.internal.OptionalProperty
/**
* @author Marcin Grzejszczak
*/
class JsonToJsonPathsConverter {
private static final Boolean SERVER_SIDE = false
private static final Boolean CLIENT_SIDE = true
public static final String ROOT_JSON_PATH_ELEMENT = '$'
public static final String ALL_ELEMENTS = "[*]"
public static JsonPaths transformToJsonPathWithTestsSideValues(def json) {
return transformToJsonPathWithValues(json, SERVER_SIDE)
}
public static JsonPaths transformToJsonPathWithStubsSideValues(def json) {
return transformToJsonPathWithValues(json, CLIENT_SIDE)
}
private static JsonPaths transformToJsonPathWithValues(def json, boolean clientSide) {
if(!json) {
return new JsonPaths()
}
JsonPaths pathsAndValues = [] as Set
Object convertedJson = MapConverter.getClientOrServerSideValues(json, clientSide)
traverseRecursivelyForKey(convertedJson, ROOT_JSON_PATH_ELEMENT) { String key, Object value ->
if (value instanceof ExecutionProperty) {
return
}
JsonPathEntry entry = getValueToInsert(key, value)
pathsAndValues.add(entry)
}
return pathsAndValues
}
protected static def traverseRecursively(Class parentType, String key, def value, Closure closure) {
if (value instanceof String && value) {
try {
def json = new JsonSlurper().parseText(value)
if (json instanceof Map) {
return convertWithKey(parentType, key, json, closure)
}
} catch (Exception ignore) {
return closure(key, value)
}
} else if (isAnEntryWithNonCollectionLikeValue(value)) {
return convertWithKey(List, key, value as Map, closure)
} else if (isAnEntryWithoutNestedStructures(value)) {
return convertWithKey(List, key, value as Map, closure)
} else if (value instanceof Map) {
return convertWithKey(Map, key, value as Map, closure)
} else if (value instanceof List) {
value.each { def element ->
traverseRecursively(List, "$key[*]", element, closure)
}
return value
}
try {
return closure(key, value)
} catch (Exception ignore) {
return value
}
}
private static boolean isAnEntryWithNonCollectionLikeValue(def value) {
if (!(value instanceof Map)) {
return false
}
Map valueAsMap = ((Map) value)
boolean mapHasOneEntry = valueAsMap.size() == 1
if (!mapHasOneEntry) {
return false
}
Object valueOfEntry = valueAsMap.entrySet().first().value
return !(valueOfEntry instanceof Map || valueOfEntry instanceof List)
}
private static boolean isAnEntryWithoutNestedStructures(def value) {
if (!(value instanceof Map)) {
return false
}
Map valueAsMap = ((Map) value)
return valueAsMap.entrySet().every { Map.Entry entry ->
[String, Number].any { entry.value.getClass().isAssignableFrom(it) }
}
}
private static Map convertWithKey(Class parentType, String parentKey, Map map, Closure closureToExecute) {
return map.collectEntries {
String entrykey, value ->
[entrykey, traverseRecursively(parentType, "${parentKey}.${entrykey}", value, closureToExecute)]
}
}
private static void traverseRecursivelyForKey(def json, String rootKey, Closure closure) {
traverseRecursively(Map, rootKey, json, closure)
}
private static JsonPathEntry getValueToInsert(String key, Object value) {
return convertToListElementFiltering(key, value)
}
protected static JsonPathEntry convertToListElementFiltering(String key, Object value) {
if (key.endsWith(ALL_ELEMENTS)) {
int lastAllElements = key.lastIndexOf(ALL_ELEMENTS)
String keyWithoutAllElements = key.substring(0, lastAllElements)
return JsonPathEntry.simple("""$keyWithoutAllElements[?(@ ${compareWith(value)})]""".toString(), value)
}
return getKeyForTraversalOfListWithNonPrimitiveTypes(key, value)
}
private static JsonPathEntry getKeyForTraversalOfListWithNonPrimitiveTypes(String key, Object value) {
int lastDot = key.lastIndexOf('.')
String keyWithoutLastElement = key.substring(0, lastDot)
String lastElement = key.substring(lastDot + 1).replaceAll(~/\[\*\]/, "")
return new JsonPathEntry(
"""$keyWithoutLastElement[?(@.$lastElement ${compareWith(value)})]""".toString(),
lastElement,
value
)
}
protected static String compareWith(Object value) {
if (value instanceof Pattern) {
return patternComparison((value as Pattern).pattern())
} else if (value instanceof OptionalProperty) {
return patternComparison((value as OptionalProperty).optionalPattern())
} else if (value instanceof GString) {
return """=~ /${RegexpBuilders.buildGStringRegexpForTestSide(value)}/"""
}
return """== ${potentiallyWrappedWithQuotesValue(value)}"""
}
protected static String patternComparison(String pattern){
return """=~ /$pattern/"""
}
protected static String potentiallyWrappedWithQuotesValue(Object value) {
return value instanceof Number ? value : "'$value'"
}
}

View File

@@ -0,0 +1,76 @@
package io.codearte.accurest.util
import groovy.json.JsonSlurper
import io.codearte.accurest.dsl.internal.DslProperty
/**
* @author Marcin Grzejszczak
*/
class MapConverter {
static def transformToClientValues(def value) {
return transformValues(value) {
it instanceof DslProperty ? it.clientValue : it
}
}
static def transformValues(def value, Closure closure) {
if (value instanceof String && value) {
try {
def json = new JsonSlurper().parseText(value)
if (json instanceof Map) {
return convert(json, closure)
}
} catch (Exception ignore) {
}
return extractValue(value, closure);
} else if (value instanceof Map) {
return convert(value as Map, closure)
} else if (value instanceof List) {
return value.collect({ transformValues(it, closure) })
}
return transformValue(closure, value)
}
protected static Object transformValue(Closure closure, Object value) {
return extractValue(value, { Object val->
Object newValue = closure(val)
if (newValue instanceof Map || newValue instanceof List || newValue instanceof String && value) {
return transformValues(newValue, closure)
}
return newValue;
})
}
private static extractValue(Object value, Closure closure) {
try {
return closure(value)
} catch (Exception ignore) {
return value
}
}
private static Map convert(Map map, Closure closure) {
return map.collectEntries {
key, value ->
[key, transformValues(value, closure)]
}
}
static Object getClientOrServerSideValues(json, boolean clientSide) {
return transformValues(json) {
if (it instanceof DslProperty) {
DslProperty dslProperty = ((DslProperty) it)
return clientSide ?
getClientOrServerSideValues(dslProperty.clientValue, clientSide) : getClientOrServerSideValues(dslProperty.serverValue, clientSide)
} else if (it instanceof GString) {
return ContentUtils.extractValue(it , null, {
if (it instanceof DslProperty) {
return clientSide ?
getClientOrServerSideValues((it as DslProperty).clientValue, clientSide) : getClientOrServerSideValues((it as DslProperty).serverValue, clientSide)
}
return it
})
}
return it
}
}
}

View File

@@ -47,7 +47,7 @@ class NamesUtil {
}
static String packageToDirectory(String packageName) {
return packageName.replaceAll('\\.', File.separator)
return packageName.replace('.' as char, File.separatorChar)
}
static String directoryToPackage(String directory) {

View File

@@ -0,0 +1,91 @@
package io.codearte.accurest.util
import io.codearte.accurest.dsl.internal.DslProperty
import org.codehaus.groovy.runtime.GStringImpl
import java.util.regex.Pattern
import static io.codearte.accurest.util.ContentUtils.extractValue
import static org.apache.commons.lang3.StringEscapeUtils.escapeJson
public class RegexpBuilders {
public static String buildGStringRegexpForStubSide(GString gString) {
new GStringImpl(
gString.values.collect(this.&buildGStringRegexpForStubSide) as Object[],
gString.strings.collect(this.&escapeSpecialRegexChars) as String[]
)
}
public static String buildGStringRegexpForStubSide(Pattern pattern) {
return pattern.pattern()
}
public static String buildGStringRegexpForStubSide(DslProperty dslProperty) {
return buildGStringRegexpForStubSide(dslProperty.clientValue)
}
public static String buildGStringRegexpForStubSide(Object o) {
return escapeSpecialRegexChars(o.toString())
}
public static String buildGStringRegexpForTestSide(GString gString) {
new GStringImpl(
gString.values.collect(this.&buildGStringRegexpForTestSide) as Object[],
gString.strings.collect(this.&escapeSpecialRegexChars) as String[]
)
}
public static String buildGStringRegexpForTestSide(Pattern pattern) {
return pattern.pattern()
}
public static String buildGStringRegexpForTestSide(DslProperty dslProperty) {
return buildGStringRegexpForTestSide(dslProperty.clientValue)
}
public static String buildGStringRegexpForTestSide(Object o) {
return o.toString().replaceAll('\\\\', '\\\\\\\\')
}
private final static Pattern SPECIAL_REGEX_CHARS = Pattern.compile('[{}()\\[\\].+*?^$\\\\|]')
private static String escapeSpecialRegexChars(String str) {
return SPECIAL_REGEX_CHARS.matcher(str).replaceAll('\\\\\\\\$0')
}
private final static String WS = /\s*/
public static String buildJSONRegexpMatch(GString gString) {
return buildJSONRegexpMatch(extractValue(gString, ContentType.JSON, { DslProperty dslProperty -> dslProperty.clientValue }))
}
public static String buildJSONRegexpMatch(Map jsonMap) {
return WS + "\\{" + jsonMap.collect(this.&buildJSONRegexpMatch).join(",") + "\\}" + WS
}
public static String buildJSONRegexpMatch(List jsonList) {
return WS + "\\[" + jsonList.collect(this.&buildJSONRegexpMatch).join(",") + "\\]" + WS
}
public static String buildJSONRegexpMatch(Map.Entry<String, Object> entry) {
return buildJSONRegexpMatchString(escapeJson(entry.key)) + ":" + buildJSONRegexpMatch(entry.value)
}
public static String buildJSONRegexpMatch(Object value) {
return buildJSONRegexpMatchStringOptionalQuotes(escapeJson(value.toString()))
}
public static String buildJSONRegexpMatch(Pattern pattern) {
return buildJSONRegexpMatchStringOptionalQuotes(pattern.pattern())
}
public static String buildJSONRegexpMatchString(String value) {
return WS + '"' + value + '"' + WS
}
public static String buildJSONRegexpMatchStringOptionalQuotes(String value) {
return WS + '"?' + value + '"?' + WS
}
}

View File

@@ -1,53 +0,0 @@
package io.codearte.accurest.util
import groovy.json.JsonException
import groovy.json.JsonSlurper
import java.util.regex.Pattern
/**
* @author Marcin Grzejszczak
*/
class StubMappingConverter {
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(/^\$\{(.*):(.*)\}$/)
public static final int SERVER_SIDE_GROUP = 2
static Map toStubMappingOnServerSide(File stubMapping) {
def json = new JsonSlurper().parse(stubMapping)
return convertPlaceholders(json as Map, { String value ->
getGroupFromMatchingPattern(value)
})
}
private static Map convertPlaceholders(Map map, Closure closure) {
return map.collectEntries {
key, value ->
[key, transformValue(value, closure)]
}
}
static def transformValue(def value, Closure closure) {
if (value instanceof String && value) {
try {
def json = new JsonSlurper().parseText(value)
if (json instanceof Map) {
return convertPlaceholders(json, closure)
}
} catch (JsonException ignore) {
return closure(value)
}
} else if (value instanceof Map) {
return convertPlaceholders(value as Map, closure)
} else if (value instanceof List) {
return value.collect({ transformValue(it, closure) })
}
return value
}
private static Object getGroupFromMatchingPattern(String value) {
return value.matches(PLACEHOLDER_PATTERN) ? PLACEHOLDER_PATTERN.matcher(value)[0][SERVER_SIDE_GROUP] : value
}
}

View File

@@ -0,0 +1,46 @@
package io.codearte.accurest.util
import groovy.transform.TypeChecked
import io.codearte.accurest.dsl.internal.DslProperty
import io.codearte.accurest.dsl.internal.MatchingStrategy
import java.util.regex.Pattern
import static io.codearte.accurest.dsl.internal.MatchingStrategy.Type.ABSENT
import static io.codearte.accurest.dsl.internal.MatchingStrategy.Type.EQUAL_TO
@TypeChecked
class ValidateUtils {
static Object validateServerValueIsAvailable(Object value) {
validateServerValueIsAvailable(value, "Server value")
return value
}
static Object validateServerValueIsAvailable(Object value, String msg) {
validateServerValue(value, msg)
return value
}
static void validateServerValue(Pattern pattern, String msg) {
throw new IllegalStateException("$msg can't be a pattern for the server side")
}
static List ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE = [EQUAL_TO, ABSENT]
static void validateServerValue(MatchingStrategy matchingStrategy, String msg) {
if (!ALLOWED_MATCHING_TYPES_ON_SERVER_SIDE.contains(matchingStrategy.type)) {
throw new IllegalStateException("$msg can't be of a matching type: $matchingStrategy.type for the server side")
}
validateServerValue(matchingStrategy.serverValue, msg)
}
static void validateServerValue(DslProperty value, String msg) {
validateServerValue(value.serverValue, msg)
}
static void validateServerValue(Object value, String msg) {
// OK
}
}

View File

@@ -0,0 +1,414 @@
package io.codearte.accurest.builder
import io.codearte.accurest.dsl.GroovyDsl
import io.codearte.accurest.dsl.WireMockStubStrategy
import io.codearte.accurest.dsl.WireMockStubVerifier
import spock.lang.Issue
import spock.lang.Specification
class JaxRsClientSpockMethodBuilderSpec extends Specification implements WireMockStubVerifier {
def "should generate assertions for simple response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """{
"property1": "a",
"property2": "b"
}"""
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
blockBuilder.toString().contains("\$[?(@.property2 == 'b')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
@Issue("#79")
def "should generate assertions for simple response body constructed from map with a list"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body(
property1: 'a',
property2: [
[a: 'sth'],
[b: 'sthElse']
]
)
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
blockBuilder.toString().contains("\$.property2[*][?(@.a == 'sth')]")
blockBuilder.toString().contains("\$.property2[*][?(@.b == 'sthElse')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
@Issue("#82")
def "should generate proper request when body constructed from map with a list"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
body(
items: ['HOP']
)
}
response {
status 200
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("entity('{\"items\":[\"HOP\"]}', 'application/json')")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
@Issue("#88")
def "should generate proper request when body constructed from GString"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
body(
"property1=VAL1"
)
}
response {
status 200
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("entity('property1=VAL1', 'application/octet-stream')")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate assertions for array in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """[
{
"property1": "a"
},
{
"property2": "b"
}]"""
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[*][?(@.property1 == 'a')]")
blockBuilder.toString().contains("\$[*][?(@.property2 == 'b')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate assertions for array inside response body element"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """{
"property1": [
{ "property2": "test1"},
{ "property3": "test2"}
]
}"""
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$.property1[*][?(@.property3 == 'test2')]")
blockBuilder.toString().contains("\$.property1[*][?(@.property2 == 'test1')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate assertions for nested objects in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body '''\
{
"property1": "a",
"property2": {"property3": "b"}
}
'''
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$.property2[?(@.property3 == 'b')]")
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate regex assertions for map objects in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body(
property1: "a",
property2: value(
client('123'),
server(regex('[0-9]{3}'))
)
)
headers {
header('Content-Type': 'application/json')
}
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]")
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate regex assertions for string objects in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body("""{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""")
headers {
header('Content-Type': 'application/json')
}
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]")
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should ignore 'Accept' header and use 'request' method"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
headers {
header("Accept", "text/plain")
}
}
response {
status 200
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("request('text/plain')")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should ignore 'Content-Type' header and use 'entity' method"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
headers {
header("Content-Type", "text/plain")
header("Timer", "123")
}
body ''
}
response {
status 200
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("entity('', 'text/plain')")
blockBuilder.toString().contains("header('Timer', '123')")
!blockBuilder.toString().contains("header('Content-Type'")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate a call with an url path and query parameters"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method 'GET'
urlPath('/users') {
queryParameters {
parameter 'limit': $(client(equalTo("20")), server(equalTo("10")))
parameter 'offset': $(client(containing("20")), server(equalTo("20")))
parameter 'filter': "email"
parameter 'sort': equalTo("name")
parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55"))
parameter 'age': $(client(notMatching("^\\w*\$")), server("99"))
parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov"))
parameter 'email': "bob@email.com"
parameter 'hello': $(client(matching("Denis.*")), server(absent()))
parameter 'hello': absent()
}
}
}
response {
status 200
body """
{
"property1": "a",
"property2": "b"
}
"""
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains("queryParam('limit', '10'")
spockTest.contains("queryParam('offset', '20'")
spockTest.contains("queryParam('filter', 'email'")
spockTest.contains("queryParam('sort', 'name'")
spockTest.contains("queryParam('search', '55'")
spockTest.contains("queryParam('age', '99'")
spockTest.contains("queryParam('name', 'Denis.Stepanov'")
spockTest.contains("queryParam('email', 'bob@email.com'")
spockTest.contains('$[?(@.property2 == \'b\')]')
spockTest.contains('$[?(@.property1 == \'a\')]')
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate test for empty body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method('POST')
url("/ws/payments")
body("")
}
response {
status 406
}
}
JaxRsClientSpockMethodBodyBuilder builder = new JaxRsClientSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains("entity('', 'application/octet-stream')")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate test for String in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "POST"
url "test"
}
response {
status 200
body "test"
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('def responseBody = (response.body.asString())')
spockTest.contains('responseBody == "test"')
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
}

View File

@@ -0,0 +1,773 @@
package io.codearte.accurest.builder
import io.codearte.accurest.dsl.GroovyDsl
import io.codearte.accurest.dsl.WireMockStubStrategy
import io.codearte.accurest.dsl.WireMockStubVerifier
import spock.lang.Issue
import spock.lang.Specification
import spock.lang.Unroll
import java.util.regex.Pattern
/**
* @author Jakub Kubrynski
*/
class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStubVerifier {
def "should generate assertions for simple response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """{
"property1": "a",
"property2": "b"
}"""
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
blockBuilder.toString().contains("\$[?(@.property2 == 'b')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
@Issue("#79")
def "should generate assertions for simple response body constructed from map with a list"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body(
property1: 'a',
property2: [
[a: 'sth'],
[b: 'sthElse']
]
)
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
blockBuilder.toString().contains("\$.property2[*][?(@.a == 'sth')]")
blockBuilder.toString().contains("\$.property2[*][?(@.b == 'sthElse')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
@Issue("#82")
def "should generate proper request when body constructed from map with a list"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
body(
items: ['HOP']
)
}
response {
status 200
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains(".body('{\"items\":[\"HOP\"]}')")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
@Issue("#88")
def "should generate proper request when body constructed from GString"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
body(
"property1=VAL1"
)
}
response {
status 200
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains(".body('property1=VAL1')")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate assertions for array in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """[
{
"property1": "a"
},
{
"property2": "b"
}]"""
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[*][?(@.property1 == 'a')]")
blockBuilder.toString().contains("\$[*][?(@.property2 == 'b')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate assertions for array inside response body element"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """{
"property1": [
{ "property2": "test1"},
{ "property3": "test2"}
]
}"""
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$.property1[*][?(@.property3 == 'test2')]")
blockBuilder.toString().contains("\$.property1[*][?(@.property2 == 'test1')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate assertions for nested objects in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body '''\
{
"property1": "a",
"property2": {"property3": "b"}
}
'''
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$.property2[?(@.property3 == 'b')]")
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate regex assertions for map objects in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body(
property1: "a",
property2: value(
client('123'),
server(regex('[0-9]{3}'))
)
)
headers {
header('Content-Type': 'application/json')
}
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]")
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate regex assertions for string objects in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body("""{"property1":"a","property2":"${value(client('123'), server(regex('[0-9]{3}')))}"}""")
headers {
header('Content-Type': 'application/json')
}
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[?(@.property2 =~ /[0-9]{3}/)]")
blockBuilder.toString().contains("\$[?(@.property1 == 'a')]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
@Issue(["#126", "#143"])
def "should generate escaped regex assertions for string objects in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body("""{"property":" ${value(client('123'), server(regex('\\d+')))}"}""")
headers {
header('Content-Type': 'application/json')
}
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("\$[?(@.property =~ /\\d+/)]")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate a call with an url path and query parameters"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method 'GET'
urlPath('/users') {
queryParameters {
parameter 'limit': $(client(equalTo("20")), server(equalTo("10")))
parameter 'offset': $(client(containing("20")), server(equalTo("20")))
parameter 'filter': "email"
parameter 'sort': equalTo("name")
parameter 'search': $(client(notMatching(~/^\/[0-9]{2}$/)), server("55"))
parameter 'age': $(client(notMatching("^\\w*\$")), server("99"))
parameter 'name': $(client(matching("Denis.*")), server("Denis.Stepanov"))
parameter 'email': "bob@email.com"
parameter 'hello': $(client(matching("Denis.*")), server(absent()))
parameter 'hello': absent()
}
}
}
response {
status 200
body """
{
"property1": "a",
"property2": "b"
}
"""
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('get("/users?limit=10&offset=20&filter=email&sort=name&search=55&age=99&name=Denis.Stepanov&email=bob@email.com")')
spockTest.contains('$[?(@.property2 == \'b\')]')
spockTest.contains('$[?(@.property1 == \'a\')]')
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate test for empty body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method('POST')
url("/ws/payments")
body("")
}
response {
status 406
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains(".body('')")
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should generate test for String in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "POST"
url "test"
}
response {
status 200
body "test"
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('def responseBody = (response.body.asString())')
spockTest.contains('responseBody == "test"')
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
@Issue('113')
def "should generate regex test for String in response header"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method 'POST'
url $(client(regex('/partners/[0-9]+/users')), server('/partners/1000/users'))
headers { header 'Content-Type': 'application/json' }
body(
first_name: 'John',
last_name: 'Smith',
personal_id: '12345678901',
phone_number: '500500500',
invitation_token: '00fec7141bb94793bfe7ae1d0f39bda0',
password: 'john'
)
}
response {
status 201
headers {
header 'Location': $(client('http://localhost/partners/1000/users/1001'), server(regex('http://localhost/partners/[0-9]+/users/[0-9]+')))
}
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('''response.header('Location') ==~ java.util.regex.Pattern.compile('http://localhost/partners/[0-9]+/users/[0-9]+')''')
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
@Issue('115')
def "should generate regex with helper method"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method 'POST'
url $(client(regex('/partners/[0-9]+/users')), server('/partners/1000/users'))
headers { header 'Content-Type': 'application/json' }
body(
first_name: 'John',
last_name: 'Smith',
personal_id: '12345678901',
phone_number: '500500500',
invitation_token: '00fec7141bb94793bfe7ae1d0f39bda0',
password: 'john'
)
}
response {
status 201
headers {
header 'Location': $(client('http://localhost/partners/1000/users/1001'), server(regex("^${hostname()}/partners/[0-9]+/users/[0-9]+")))
}
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('''response.header('Location') ==~ java.util.regex.Pattern.compile('^((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?/partners/[0-9]+/users/[0-9]+')''')
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should work with more complex stuff and jsonpaths"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
priority 10
request {
method 'POST'
url '/validation/client'
headers {
header 'Content-Type': 'application/json'
}
body(
bank_account_number: '0014282912345698765432161182',
email: 'foo@bar.com',
phone_number: '100299300',
personal_id: 'ABC123456'
)
}
response {
status 200
body(errors: [
[property: "bank_account_number", message: "incorrect_format"]
])
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('''$.errors[*][?(@.property == 'bank_account_number')]''')
spockTest.contains('''$.errors[*][?(@.message == 'incorrect_format')]''')
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should work properly with GString url"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method 'PUT'
url "/partners/${value(client(regex('^[0-9]*$')), server('11'))}/agents/11/customers/09665703Z"
headers {
header 'Content-Type': 'application/json'
}
body(
first_name: 'Josef',
)
}
response {
status 422
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('''/partners/11/agents/11/customers/09665703Z''')
and:
stubMappingIsValidWireMockStub(new WireMockStubStrategy(contractDsl).toWireMockClientStub())
}
def "should resolve properties in GString with regular expression"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
}
body(
email: $(client(regex(email())), server('not.existing@user.com')),
callback_url: $(client(regex(hostname())), server('http://partners.com'))
)
}
response {
status 404
headers {
header 'Content-Type': 'application/json'
}
body(
code: 4,
message: "User not found by email = [${value(server(regex(email())), client('not.existing@user.com'))}]"
)
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('''$[?(@.message =~ /User not found by email = \\\\[[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4}\\\\]/)]''')
}
@Issue('42')
@Unroll
def "should not omit the optional field in the test creation"() {
given:
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('''"email":"abc@abc.com"''')
spockTest.contains('''parsedJson.read(\'\'\'$[?(@.code =~ /(123123)?/)]''')
!spockTest.contains('''REGEXP''')
!spockTest.contains('''OPTIONAL''')
!spockTest.contains('''OptionalProperty''')
where:
contractDsl << [
GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
}
body(
email: $(stub(optional(regex(email()))), test('abc@abc.com')),
callback_url: $(stub(regex(hostname())), test('http://partners.com'))
)
}
response {
status 404
headers {
header 'Content-Type': 'application/json'
}
body(
code: value(stub("123123"), test(optional("123123"))),
message: "User not found by email = [${value(test(regex(email())), stub('not.existing@user.com'))}]"
)
}
},
GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
}
body(
""" {
"email" : "${value(stub(optional(regex(email()))), test('abc@abc.com'))}",
"callback_url" : "${value(client(regex(hostname())), server('http://partners.com'))}"
}
"""
)
}
response {
status 404
headers {
header 'Content-Type': 'application/json'
}
body(
""" {
"code" : "${value(stub(123123), test(optional(123123)))}",
"message" : "User not found by email = [${value(server(regex(email())), client('not.existing@user.com'))}]"
}
"""
)
}
}
]
}
@Issue('72')
def "should make the execute method work"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method """PUT"""
url """/fraudcheck"""
body("""
{
"clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}",
"loanAmount":123.123
}
"""
)
headers {
header("""Content-Type""", """application/vnd.fraud.v1+json""")
}
}
response {
status 200
body( """{
"fraudCheckStatus": "OK",
"rejectionReason": ${value(client(null), server(execute('assertThatRejectionReasonIsNull($it)')))}
}""")
headers {
header('Content-Type': 'application/vnd.fraud.v1+json')
}
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains('''assertThatRejectionReasonIsNull(parsedJson.read('$.rejectionReason'))''')
}
def "should support inner map and list definitions"() {
given:
Pattern PHONE_NUMBER = Pattern.compile(/[+\w]*/)
Pattern ANYSTRING = Pattern.compile(/.*/)
Pattern NUMBERS = Pattern.compile(/[\d\.]*/)
Pattern DATETIME = ANYSTRING
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "PUT"
url "/v1/payments/e86df6f693de4b35ae648464c5b0dc09/client_data"
headers {
header('Content-Type': 'application/json')
}
body(
client: [
first_name: $(stub(regex(onlyAlphaUnicode())), test('Denis')),
last_name: $(stub(regex(onlyAlphaUnicode())), test('FakeName')),
email: $(stub(regex(email())), test('fakemail@fakegmail.com')),
fax: $(stub(PHONE_NUMBER), test('+xx001213214')),
phone: $(stub(PHONE_NUMBER), test('2223311')),
data_of_birth: $(stub(DATETIME), test('2002-10-22T00:00:00Z'))
],
client_id_card: [
id: $(stub(ANYSTRING), test('ABC12345')),
date_of_issue: $(stub(ANYSTRING), test('2002-10-02T00:00:00Z')),
address: [
street: $(stub(ANYSTRING), test('Light Street')),
city: $(stub(ANYSTRING), test('Fire')),
region: $(stub(ANYSTRING), test('Skys')),
country: $(stub(ANYSTRING), test('HG')),
zip: $(stub(NUMBERS), test('658965'))
]
],
incomes_and_expenses: [
monthly_income: $(stub(NUMBERS), test('0.0')),
monthly_loan_repayments: $(stub(NUMBERS), test('100')),
monthly_living_expenses: $(stub(NUMBERS), test('22'))
],
additional_info: [
allow_to_contact: $(stub(optional(regex(anyBoolean()))), test('true'))
]
)
}
response {
status 200
headers {
header('Content-Type': 'application/json')
}
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
spockTest.contains '"street":"Light Street"'
!spockTest.contains("clientValue")
!spockTest.contains("cursor")
}
def "shouldn't generate unicode escape characters"() {
given:
Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/)
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "PUT"
url "/v1/payments/e86df6f693de4b35ae648464c5b0dc09/енев"
headers {
header('Content-Type': 'application/json')
}
body(
client: [
first_name: $(stub(ONLY_ALPHA_UNICODE), test('Пенева')),
last_name : $(stub(ONLY_ALPHA_UNICODE), test('Пенева'))
]
)
}
response {
status 200
headers {
header('Content-Type': 'application/json')
}
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
!spockTest.contains("\\u041f")
}
}

View File

@@ -1,113 +0,0 @@
package io.codearte.accurest.builder
import io.codearte.accurest.dsl.GroovyDsl
import spock.lang.Specification
/**
* @author Jakub Kubrynski
*/
class SpockMethodBuilderSpec extends Specification {
def "should generate assertions for simple response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """{
"property1": "a",
"property2": "b"
}"""
}
}
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("responseBody.property1 == \"a\"")
blockBuilder.toString().contains("responseBody.property2 == \"b\"")
}
def "should generate assertions for array in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """[
{
"property1": "a"
},
{
"property2": "b"
}]"""
}
}
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("responseBody[0].property1 == \"a\"")
blockBuilder.toString().contains("responseBody[1].property2 == \"b\"")
}
def "should generate assertions for array inside response body element"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body """{
"property1": [
{ "property2": "test1"},
{ "property3": "test2"}
]
}"""
}
}
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("responseBody.property1[0].property2 == \"test1\"")
blockBuilder.toString().contains("responseBody.property1[1].property3 == \"test2\"")
}
def "should generate assertions for nested objects in response body"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
request {
method "GET"
url "test"
}
response {
status 200
body '''\
{
"property1": "a",
"property2": {"property3": "b"}
}
'''
}
}
SpockMethodBodyBuilder builder = new SpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
then:
blockBuilder.toString().contains("responseBody.property1 == \"a\"")
blockBuilder.toString().contains("responseBody.property2.property3 == \"b\"")
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
package io.codearte.accurest.dsl
import com.github.tomakehurst.wiremock.stubbing.StubMapping
import java.util.regex.Pattern
trait WireMockStubVerifier {
void stubMappingIsValidWireMockStub(String mappingDefinition) {
StubMapping stubMapping = StubMapping.buildFrom(mappingDefinition)
stubMapping.request.bodyPatterns.findAll { it.matches }.every {
Pattern.compile(it.matches)
}
assert !mappingDefinition.contains('io.codearte.accurest.dsl.internal')
}
}

View File

@@ -1,52 +0,0 @@
package io.codearte.accurest.dsl
import groovy.json.JsonSlurper
import spock.lang.Specification
class WiremockGroovyDslResponseSpec extends Specification {
def 'should generate response without body for client side'() {
given:
GroovyDsl dsl = GroovyDsl.make {
response {
status 200
}
}
expect:
new WiremockResponseStubStrategy(dsl).buildClientResponseContent() == new JsonSlurper().parseText(expectedStub)
where:
expectedStub << ['''
{
"status": 200
}
''',
'''
{
"status": 200
}
''']
}
def 'should generate headers for response for client side'() {
given:
GroovyDsl dsl = GroovyDsl.make {
response {
headers {
header 'Content-Type', $(client('text/xml'), server('text/*'))
}
status 200
}
}
expect:
new WiremockResponseStubStrategy(dsl).buildClientResponseContent() == new JsonSlurper().parseText('''
{
"headers": {
"Content-Type": "text/xml"
},
"status": 200
}
''')
}
}

View File

@@ -1,289 +0,0 @@
package io.codearte.accurest.dsl
import groovy.json.JsonSlurper
class WiremockGroovyDslSpec extends WiremockSpec {
def 'should convert groovy dsl stub to wiremock stub for the client side'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method('GET')
url $(client(~/\/[0-9]{2}/), server('/12'))
}
response {
status 200
body(
id: value(
client('123'),
server({ regex('[0-9]+') })
),
surname: $(
client('Kowalsky'),
server('Lewandowski')
),
name: 'Jan',
created: $(client('2014-02-02 12:23:43'), server({ currentDate(it) }))
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}"
},
"response": {
"status": 200,
"body": "{\\"id\\":\\"123\\",\\"surname\\":\\"Kowalsky\\",\\"name\\":\\"Jan\\",\\"created\\":\\"2014-02-02 12:23:43\\"}",
"headers": {
"Content-Type": "text/plain"
}
}
}
''')
and:
stubMappingIsValidWiremockStub(wiremockStub)
}
def 'should convert groovy dsl stub with Body as String to wiremock stub for the client side'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method('GET')
url $(client(~/\/[0-9]{2}/), server('/12'))
}
response {
status 200
body("""\
{
"id": "${value(client('123'), server('321'))}",
"surname": "${value(client('Kowalsky'), server('Lewandowski'))}",
"name": "Jan",
"created" : "${$(client('2014-02-02 12:23:43'), server('2999-09-09 01:23:45'))}"
}
"""
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}"
},
"response": {
"status": 200,
"body": "{\\"created\\":\\"2014-02-02 12:23:43\\",\\"id\\":\\"123\\",\\"name\\":\\"Jan\\",\\"surname\\":\\"Kowalsky\\"}",
"headers": {
"Content-Type": "text/plain"
}
}
}
''')
and:
stubMappingIsValidWiremockStub(wiremockStub)
}
def 'should convert groovy dsl stub with simple Body as String to wiremock stub for the client side'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method('GET')
url $(client(regex('/[0-9]{2}')), server('/12'))
body """
{
"name": "Jan"
}
"""
}
response {
status 200
body("""\
{
"name": "Jan"
}
"""
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}",
"bodyPatterns": [
{
"equalTo":"{\\"name\\":\\"Jan\\"}"
}
]
},
"response": {
"status": 200,
"body": "{\\"name\\":\\"Jan\\"}",
"headers": {
"Content-Type": "text/plain"
}
}
}
''')
and:
stubMappingIsValidWiremockStub(wiremockStub)
}
def 'should convert groovy dsl stub with regexp Body as String to wiremock stub for the client side'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method('GET')
url $(client(regex('/[0-9]{2}')), server('/12'))
body """
{
"personalId": "${value(client(regex('^[0-9]{11}$')), server('57593728525'))}"
}
"""
}
response {
status 200
body("""\
{
"name": "Jan"
}
"""
)
headers {
header 'Content-Type': 'text/plain'
}
}
}
when:
String wiremockStub = new WiremockStubStrategy(groovyDsl).toWiremockClientStub()
then:
new JsonSlurper().parseText(wiremockStub) == new JsonSlurper().parseText('''
{
"request": {
"method": "GET",
"urlPattern": "/[0-9]{2}",
"bodyPatterns": [
{
"matches":"{\\"personalId\\":\\"^[0-9]{11}$\\"}"
}
]
},
"response": {
"status": 200,
"body": "{\\"name\\":\\"Jan\\"}",
"headers": {
"Content-Type": "text/plain"
}
}
}
''')
and:
stubMappingIsValidWiremockStub(wiremockStub)
}
def "should generate stub with GET"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method("GET")
}
}
expect:
new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText('''
{
"method":"GET"
}
''')
}
def "should generate request when two elements are provided "() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
method("GET")
url("/sth")
}
}
expect:
new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText('''
{
"method":"GET",
"url":"/sth"
}
''')
}
def "should generate request with urlPattern for client side"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
url $(
client(~/\/^[0-9]{2}$/),
server('/12')
)
}
}
expect:
new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText('''
{
"urlPattern":"/^[0-9]{2}$"
}
''')
}
def "should generate stub with some headers section for client side"() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
request {
headers {
header('Content-Type': 'text/xml')
header('Accept': $(
client(regex('text/.*')),
server('text/plain')
))
header('X-Custom-Header': $(
client(regex('^.*2134.*$')),
server('121345')
))
}
}
}
expect:
new WiremockRequestStubStrategy(groovyDsl).buildClientRequestContent() == new JsonSlurper().parseText('''
{
"headers": {
"Content-Type": {
"equalTo": "text/xml"
},
"Accept": {
"matches": "text/.*"
},
"X-Custom-Header": {
"matches": "^.*2134.*$"
}
}
}
''')
}
}

View File

@@ -1,12 +0,0 @@
package io.codearte.accurest.dsl
import com.github.tomakehurst.wiremock.stubbing.StubMapping
import spock.lang.Specification
class WiremockSpec extends Specification {
void stubMappingIsValidWiremockStub(String mappingDefinition) {
StubMapping.buildFrom(mappingDefinition)
}
}

View File

@@ -4,16 +4,16 @@ import spock.lang.Specification
class ExecutionPropertySpec extends Specification {
def 'should insert passed value in place of $it placeholder'() {
given:
String commandToExecute = 'commandToExecute($it)'
ExecutionProperty executionProperty = new ExecutionProperty(commandToExecute)
and:
String valueToInsert = 'someObject.itsValue'
when:
String commandWithInsertedValue = executionProperty.insertValue(valueToInsert)
then:
'commandToExecute(someObject.itsValue)' == commandWithInsertedValue
}
def 'should insert passed value in place of $it placeholder'() {
given:
String commandToExecute = 'commandToExecute($it)'
ExecutionProperty executionProperty = new ExecutionProperty(commandToExecute)
and:
String valueToInsert = 'someObject.itsValue'
when:
String commandWithInsertedValue = executionProperty.insertValue(valueToInsert)
then:
'commandToExecute(someObject.itsValue)' == commandWithInsertedValue
}
}

View File

@@ -0,0 +1,68 @@
package io.codearte.accurest.dsl.internal
import spock.lang.Specification
import spock.lang.Unroll
import java.util.regex.Pattern
class RegexPatternsSpec extends Specification {
RegexPatterns regexPatterns = new RegexPatterns()
@Unroll
def "should generate a regex for ip address [#textToMatch] that is a match [#shouldMatch]"() {
expect:
shouldMatch == Pattern.compile(regexPatterns.ipAddress()).matcher(textToMatch).matches()
where:
textToMatch || shouldMatch
'123.123.123.123' || true
'a.b.' || false
}
@Unroll
def "should generate a regex for hostname [#textToMatch] that is a match [#shouldMatch]"() {
expect:
shouldMatch == Pattern.compile(regexPatterns.hostname()).matcher(textToMatch).matches()
where:
textToMatch || shouldMatch
'https://asd.com' || true
'https://asd.com:8080' || true
'https://localhost' || true
'https://localhost:8080' || true
'https://asd.com/asd' || false
'asd.com' || false
}
@Unroll
def "should generate a regex for email [#textToMatch] that is a match [#shouldMatch]"() {
expect:
shouldMatch == Pattern.compile(regexPatterns.email()).matcher(textToMatch).matches()
where:
textToMatch || shouldMatch
'asd@asd.com' || true
'a.b.' || false
}
@Unroll
def "should generate a regex for url [#textToMatch] that is a match [#shouldMatch]"() {
expect:
shouldMatch == Pattern.compile(regexPatterns.url()).matcher(textToMatch).matches()
where:
textToMatch || shouldMatch
'ftp://asd.com:9090/asd/a?a=b' || true
'a.b.' || false
}
@Unroll
def "should generate a regex for a number [#textToMatch] that is a match [#shouldMatch]"() {
expect:
shouldMatch == Pattern.compile(regexPatterns.number()).matcher(textToMatch).matches()
where:
textToMatch || shouldMatch
'1' || true
'1.0' || true
'0.1' || true
'.1' || true
'1.' || false
}
}

View File

@@ -0,0 +1,199 @@
package io.codearte.accurest.util
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import com.jayway.jsonpath.Configuration
import com.jayway.jsonpath.DocumentContext
import com.jayway.jsonpath.JsonPath
import com.jayway.jsonpath.Option
import net.minidev.json.JSONArray
import spock.lang.Specification
import spock.lang.Unroll
import java.util.regex.Pattern
class JsonToJsonPathsConverterSpec extends Specification {
@Unroll
def 'should convert a json with list as root to a map of path to value'() {
when:
JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json))
then:
pathAndValues['''$[*].some.nested[?(@.json == 'with value')]'''] == 'with value'
pathAndValues['''$[*].some.nested[?(@.anothervalue == 4)]'''] == 4
pathAndValues['''$[*].some.nested.withlist[*][?(@.name == 'name1')]'''] == 'name1'
pathAndValues['''$[*].some.nested.withlist[*][?(@.name == 'name2')]'''] == 'name2'
pathAndValues['''$[*].some.nested.withlist[*].anothernested[?(@.name == 'name3')]'''] == 'name3'
and:
assertThatJsonPathsInMapAreValid(json, pathAndValues)
where:
json << [
'''
[ {
"some" : {
"nested" : {
"json" : "with value",
"anothervalue": 4,
"withlist" : [
{ "name" :"name1"} , {"name": "name2"}, {"anothernested": { "name": "name3"} }
]
}
}
},
{
"someother" : {
"nested" : {
"json" : "with value",
"anothervalue": 4,
"withlist" : [
{ "name" :"name1"} , {"name": "name2"}
]
}
}
}
]
''',
'''
[{
"someother" : {
"nested" : {
"json" : "with value",
"anothervalue": 4,
"withlist" : [
{ "name" :"name1"} , {"name": "name2"}
]
}
}
},
{
"some" : {
"nested" : {
"json" : "with value",
"anothervalue": 4,
"withlist" : [
{"name": "name2"}, {"anothernested": { "name": "name3"} }, { "name" :"name1"}
]
}
}
}
]''']
}
def 'should convert a json with a map as root to a map of path to value'() {
given:
String json = '''
{
"some" : {
"nested" : {
"json" : "with value",
"anothervalue": 4,
"withlist" : [
{ "name" :"name1"} , {"name": "name2"}
]
}
}
}
'''
when:
JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json))
then:
pathAndValues['''$.some.nested[?(@.json == 'with value')]'''] == 'with value'
pathAndValues['''$.some.nested[?(@.anothervalue == 4)]'''] == 4
pathAndValues['''$.some.nested.withlist[*][?(@.name == 'name1')]'''] == 'name1'
pathAndValues['''$.some.nested.withlist[*][?(@.name == 'name2')]'''] == 'name2'
and:
assertThatJsonPathsInMapAreValid(json, pathAndValues)
}
def 'should convert a json with a list'() {
given:
String json = '''
{
"items" : ["HOP"]
}
'''
when:
JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json))
then:
pathAndValues['''$.items[?(@ == 'HOP')]'''] == 'HOP'
and:
assertThatJsonPathsInMapAreValid(json, pathAndValues)
}
def 'should convert a json with a list of errors'() {
given:
String json = '''
{
"errors" : [
{ "property" : "email", "message" : "inconsistent value" },
{ "property" : "email", "message" : "inconsistent value2" }
]
}
'''
when:
JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(new JsonSlurper().parseText(json))
then:
pathAndValues['''$.errors[*][?(@.property == 'email')]'''] == 'email'
pathAndValues['''$.errors[*][?(@.message == 'inconsistent value')]'''] == 'inconsistent value'
pathAndValues['''$.errors[*][?(@.message == 'inconsistent value2')]'''] == 'inconsistent value2'
pathAndValues['''$.errors[*][?(@.property == 'email')]'''] == 'email'
and:
assertThatJsonPathsInMapAreValid(json, pathAndValues)
}
def 'should convert a map json with a regex pattern'() {
given:
List json = [
[some:
[nested: [
json: "with value",
anothervalue: 4,
withlist:
[
[name: "name2"],
[name: "name1"],
[anothernested:
[name: Pattern.compile('[a-zA-Z]+')]
],
[age: "123456789"]
]
]
]
],
[someother:
[nested: [
json: "with value",
anothervalue: 4,
withlist:
[
[name: "name2"],
[name: "name1"]
]
]
]
]
]
when:
JsonPaths pathAndValues = JsonToJsonPathsConverter.transformToJsonPathWithTestsSideValues(json)
then:
pathAndValues['''$[*].some.nested[?(@.json == 'with value')]'''] == 'with value'
pathAndValues['''$[*].some.nested[?(@.anothervalue == 4)]'''] == 4
pathAndValues['''$[*].some.nested.withlist[*][?(@.name == 'name1')]'''] == 'name1'
pathAndValues['''$[*].some.nested.withlist[*][?(@.name == 'name2')]''']
(pathAndValues['''$[*].some.nested.withlist[*].anothernested[?(@.name =~ /[a-zA-Z]+/)]'''] as Pattern).pattern() == '[a-zA-Z]+'
when:
pathAndValues['''$[*].some.nested.withlist[*].anothernested[?(@.name =~ /[a-zA-Z]+/)]'''] = "Kowalski"
json.some.nested.withlist[0][2].anothernested.name = "Kowalski"
then:
assertThatJsonPathsInMapAreValid(JsonOutput.prettyPrint(JsonOutput.toJson(json)), pathAndValues)
}
private void assertThatJsonPathsInMapAreValid(String json, JsonPaths pathAndValues) {
DocumentContext parsedJson = JsonPath.using(Configuration.builder().options(Option.ALWAYS_RETURN_LIST).build()).parse(json);
pathAndValues.each {
assert parsedJson.read(it.jsonPath, JSONArray).getAt(it.optionalSuffix ?: 0) == it.optionalSuffix ? [it.value] : it.value
}
}
}

View File

@@ -5,23 +5,23 @@ io.codearte.accurest.dsl.GroovyDsl.make {
header 'Content-Type': 'application/json'
}
body("""\
{
"name": "Jan",
"id": "${value(client('abc'), server('def'))}",
}
"""
{
"name": "Jan",
"id": "${value(client('abc'), server('def'))}",
}
"""
)
url $(client('/[0-9]{2}'), server('/12'))
}
response {
status 200
body("""\
{
"name": "Jan",
"id": "${value(client('123'), server('321'))}",
{
"name": "Jan",
"id": "${value(client('123'), server('321'))}",
"surname": "${value(client('Kowalsky'), server('$checkIfSurnameValid($value)'))}"
}
"""
}
"""
)
headers {
header 'Content-Type': 'text/plain'

View File

@@ -4,6 +4,7 @@ import io.codearte.accurest.config.AccurestConfigProperties
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.DependencyResolveDetails
/**
* @author Jakub Kubrynski
@@ -11,7 +12,7 @@ import org.gradle.api.Task
class AccurestGradlePlugin implements Plugin<Project> {
private static final String GENERATE_SERVER_TESTS_TASK_NAME = 'generateAccurest'
private static final String DSL_TO_WIREMOCK_CLIENT_TASK_NAME = 'generateWiremockClientStubs'
private static final String DSL_TO_WIREMOCK_CLIENT_TASK_NAME = 'generateWireMockClientStubs'
private static final Class IDEA_PLUGIN_CLASS = org.gradle.plugins.ide.idea.IdeaPlugin
private static final String GROUP_NAME = "Verification"
@@ -27,7 +28,9 @@ class AccurestGradlePlugin implements Plugin<Project> {
setConfigurationDefaults(extension)
createGenerateTestsTask(extension)
createAndConfigureGenerateWiremockClientStubsFromDslTask(extension)
createAndConfigureGenerateWireMockClientStubsFromDslTask(extension)
deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask()
project.dependencies.add("testCompile", "com.github.tomakehurst:wiremock:2.0.5-beta")
project.afterEvaluate {
def hasIdea = project.plugins.findPlugin(IDEA_PLUGIN_CLASS)
@@ -60,13 +63,21 @@ class AccurestGradlePlugin implements Plugin<Project> {
}
}
private void createAndConfigureGenerateWiremockClientStubsFromDslTask(AccurestConfigProperties extension) {
Task task = project.tasks.create(DSL_TO_WIREMOCK_CLIENT_TASK_NAME, GenerateWiremockClientStubsFromDslTask)
task.description = "Generate Wiremock client stubs from GroovyDSL"
private void createAndConfigureGenerateWireMockClientStubsFromDslTask(AccurestConfigProperties extension) {
Task task = project.tasks.create(DSL_TO_WIREMOCK_CLIENT_TASK_NAME, GenerateWireMockClientStubsFromDslTask)
task.description = "Generate WireMock client stubs from GroovyDSL"
task.group = GROUP_NAME
task.conventionMapping.with {
contractsDslDir = { extension.contractsDslDir }
stubsOutputDir = { extension.stubsOutputDir }
}
}
private void deprecatedCreateAndConfigureGenerateWiremockClientStubsFromDslTask() {
Task task = project.tasks.create('generateWiremockClientStubs')
task.dependsOn('generateWireMockClientStubs')
task.description = "DEPRECATED - Generates WireMock client stubs. - DEPRECATED - use 'generateWireMockClientStubs' task"
task.group = GROUP_NAME
task.doFirst {logger.warn("DEPRECATION WARNING. Task 'generateWiremockClientStubs' is deprecated. Use 'generateWireMockClientStubs' task instead.")}
}
}

View File

@@ -1,6 +1,6 @@
package io.codearte.accurest.plugin
import io.codearte.accurest.wiremock.DslToWiremockClientConverter
import io.codearte.accurest.wiremock.DslToWireMockClientConverter
import io.codearte.accurest.wiremock.RecursiveFilesConverter
import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.InputDirectory
@@ -8,7 +8,7 @@ import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
//TODO: Implement as an incremental task: https://gradle.org/docs/current/userguide/custom_tasks.html#incremental_tasks ?
class GenerateWiremockClientStubsFromDslTask extends ConventionTask {
class GenerateWireMockClientStubsFromDslTask extends ConventionTask {
@InputDirectory
File contractsDslDir
@@ -17,10 +17,10 @@ class GenerateWiremockClientStubsFromDslTask extends ConventionTask {
@TaskAction
void generate() {
project.logger.info("Accurest Plugin: Invoking GroovyDSL to Wiremock client stubs conversion")
project.logger.debug("From '${getContractsDslDir()}' to '${getStubsOutputDir()}'")
logger.info("Accurest Plugin: Invoking GroovyDSL to WireMock client stubs conversion")
logger.debug("From '${getContractsDslDir()}' to '${getStubsOutputDir()}'")
RecursiveFilesConverter converter = new RecursiveFilesConverter(new DslToWiremockClientConverter(), getContractsDslDir(),
RecursiveFilesConverter converter = new RecursiveFilesConverter(new DslToWireMockClientConverter(), getContractsDslDir(),
getStubsOutputDir())
converter.processFiles()
}

View File

@@ -1,6 +1,6 @@
package io.codearte.accurest.plugin
import groovy.json.JsonSlurper
import io.codearte.accurest.util.AssertionUtil
import nebula.test.IntegrationSpec
import spock.lang.Stepwise
@@ -23,7 +23,7 @@ class BasicFunctionalSpec extends IntegrationSpec {
when:
def result = runTasksSuccessfully('check')
then:
result.wasExecuted(":generateWiremockClientStubs")
result.wasExecuted(":generateWireMockClientStubs")
result.wasExecuted(":generateAccurest")
and: "tests generated"
@@ -38,28 +38,29 @@ class BasicFunctionalSpec extends IntegrationSpec {
def "should generate valid client json stubs for simple input"() {
when:
runTasksSuccessfully('generateWiremockClientStubs')
runTasksSuccessfully('generateWireMockClientStubs')
then:
def generatedClientJsonStub = file(GENERATED_CLIENT_JSON_STUB).text
new JsonSlurper().parseText(generatedClientJsonStub) == new JsonSlurper().parseText("""
{
"request": {
"method": "PUT",
"headers": {
"Content-Type": {
"equalTo": "application/json"
}
},
"url": "/api/12",
"bodyPatterns": [
{ "equalTo": "[{\\"text\\":\\"Gonna see you at Warsaw\\"}]" }
]
},
"response": {
"status": 200
}
}
""")
AssertionUtil.assertThatJsonsAreEqual("""
{
"request" : {
"url" : "/api/12",
"method" : "PUT",
"bodyPatterns" : [ {
"matchesJsonPath" : "\$[*][?(@.text == 'Gonna see you at Warsaw')]"
} ],
"headers" : {
"Content-Type" : {
"equalTo" : "application/json"
}
}
},
"response" : {
"status" : 200
},
"priority" : 2
}
""", generatedClientJsonStub)
}
def "tasks should be up-to-date when appropriate"() {
@@ -67,16 +68,16 @@ class BasicFunctionalSpec extends IntegrationSpec {
assert !fileExists(GENERATED_CLIENT_JSON_STUB)
assert !fileExists(TEST_EXECUTION_XML_REPORT)
when:
runTasksSuccessfully('generateWiremockClientStubs', 'generateAccurest')
runTasksSuccessfully('generateWireMockClientStubs', 'generateAccurest')
then:
fileExists(GENERATED_CLIENT_JSON_STUB)
fileExists(GENERATED_TEST)
when: "running generation without change inputs"
def secondExecutionResult = runTasksSuccessfully('generateWiremockClientStubs', 'generateAccurest')
def secondExecutionResult = runTasksSuccessfully('generateWireMockClientStubs', 'generateAccurest')
then: "tasks should be up-to-date"
secondExecutionResult.wasUpToDate(":generateWiremockClientStubs")
secondExecutionResult.wasUpToDate(":generateWireMockClientStubs")
secondExecutionResult.wasUpToDate(":generateAccurest")
when: "inputs changed"
@@ -84,10 +85,10 @@ class BasicFunctionalSpec extends IntegrationSpec {
groovyDslFile.text = groovyDslFile.text.replace("200", "599")
and: "tasks run"
def thirdExecutionResult = runTasksSuccessfully('generateWiremockClientStubs', 'generateAccurest')
def thirdExecutionResult = runTasksSuccessfully('generateWireMockClientStubs', 'generateAccurest')
then: "tasks should be reexecuted"
thirdExecutionResult.wasExecuted(":generateWiremockClientStubs")
thirdExecutionResult.wasExecuted(":generateWireMockClientStubs")
thirdExecutionResult.wasExecuted(":generateAccurest")
and: "changes visible in generate files"

View File

@@ -0,0 +1,21 @@
package io.codearte.accurest.plugin
import nebula.test.IntegrationSpec
import spock.lang.Stepwise
@Stepwise
class SampleJerseyProjectSpec extends IntegrationSpec {
void setup() {
copyResources("functionalTest/sampleJerseyProject", "")
runTasksSuccessfully('clean') //delete accidental output when previously importing SimpleBoot into Idea to tweak it
}
def "should pass basic flow"() {
given:
assert fileExists('build.gradle')
expect:
runTasksSuccessfully('check')
}
}

View File

@@ -0,0 +1,21 @@
package io.codearte.accurest.plugin
import nebula.test.IntegrationSpec
import spock.lang.Stepwise
@Stepwise
class SampleProjectSpec extends IntegrationSpec {
void setup() {
copyResources("functionalTest/sampleProject", "")
runTasksSuccessfully('clean') //delete accidental output when previously importing SimpleBoot into Idea to tweak it
}
def "should pass basic flow"() {
given:
assert fileExists('build.gradle')
expect:
runTasksSuccessfully('check')
}
}

View File

@@ -1,75 +1,66 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "io.codearte.accurest:accurest-gradle-plugin:$accurestVersion"
}
repositories {
mavenCentral()
}
}
apply plugin: 'groovy'
apply plugin: 'accurest'
ext {
contractsDir = file("${project.rootDir}/repository/mappings/com/ofg/twitter-places-analyzer")
wiremockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/")
wiremockStubsOutputDir = new File(wiremockStubsOutputDirRoot, 'repository/mappings/')
contractsDir = file("${project.rootDir}/repository/mappings/com/ofg/twitter-places-analyzer")
wireMockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/")
wireMockStubsOutputDir = new File(wireMockStubsOutputDirRoot, 'repository/mappings/')
}
configurations {
all {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
// To prevent an accidental usage of groovy-all.jar and groovy.jar in different versions
// all modularized Groovy jars are replaced with groovy-all.jar by default.
if (details.requested.group == 'org.codehaus.groovy' && details.requested.name != "groovy-all") {
details.useTarget("org.codehaus.groovy:groovy-all:${details.requested.version}")
}
}
}
}
all {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
// To prevent an accidental usage of groovy-all.jar and groovy.jar in different versions
// all modularized Groovy jars are replaced with groovy-all.jar by default.
if (details.requested.group == 'org.codehaus.groovy' && details.requested.name != "groovy-all") {
details.useTarget("org.codehaus.groovy:groovy-all:${details.requested.version}")
}
}
}
}
}
repositories {
mavenCentral()
mavenCentral()
}
dependencies {
compile "org.springframework:spring-web:$springVersion"
compile "org.springframework:spring-context-support:$springVersion"
compile "org.codehaus.groovy:groovy-all:2.4.1"
compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4'
compile "org.codehaus.jackson:jackson-mapper-asl:$jacksonMapper"
compile "org.codehaus.jackson:jackson-core-asl:$jacksonMapper"
compile 'com.jayway.jsonpath:json-path-assert:1.2.0'
compile "org.springframework:spring-web:$springVersion"
compile "org.springframework:spring-context-support:$springVersion"
compile "org.codehaus.groovy:groovy-all:2.4.5"
compile 'com.fasterxml.jackson.core:jackson-databind:2.4.4'
compile 'com.jayway.jsonpath:json-path-assert:2.0.0'
testCompile('com.github.tomakehurst:wiremock:1.53') {
exclude group: 'org.mortbay.jetty', module: 'servlet-api'
}
testCompile "org.spockframework:spock-spring:0.7-groovy-2.0"
testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion"
testCompile "com.jayway.restassured:spring-mock-mvc:$restAssuredVersion"
testCompile "io.codearte.accurest:accurest-core:$accurestVersion"
testCompile "javax.servlet:javax.servlet-api:3.0.1" //provided
testCompile "ch.qos.logback:logback-classic:1.1.2"
testCompile "com.github.tomakehurst:wiremock:2.0.5-beta"
testCompile "org.spockframework:spock-spring:1.0-groovy-2.4"
testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion"
testCompile "com.jayway.restassured:spring-mock-mvc:$restAssuredVersion"
testCompile "ch.qos.logback:logback-classic:1.1.2"
}
accurest {
baseClassForTests = 'com.ofg.twitter.places.BaseMockMvcSpec'
basePackageForTests = 'accurest'
contractsDslDir = contractsDir
// generatedTestSourcesDir = file("${project.rootDir}/src/test/groovy/")
stubsOutputDir = wiremockStubsOutputDir
baseClassForTests = 'com.ofg.twitter.places.BaseMockMvcSpec'
basePackageForTests = 'accurest'
contractsDslDir = contractsDir
// generatedTestSourcesDir = file("${project.rootDir}/src/test/groovy/")
stubsOutputDir = wireMockStubsOutputDir
}
//TODO: Put it into the plugin
task createWiremockStubsOutputDir << {
wiremockStubsOutputDir.mkdirs()
task createWireMockStubsOutputDir << {
wireMockStubsOutputDir.mkdirs()
}
generateWiremockClientStubs.dependsOn { createWiremockStubsOutputDir }
generateAccurest.dependsOn generateWiremockClientStubs
generateWireMockClientStubs.dependsOn { createWireMockStubsOutputDir }
generateAccurest.dependsOn generateWireMockClientStubs
wrapper {
gradleVersion '2.2.1'
gradleVersion '2.2.1'
}

View File

@@ -1,5 +1,4 @@
groupId=com.ofg
jacksonMapper=1.9.13
restAssuredVersion=2.4.0
accurestVersion=0.4.1
springVersion=4.1.4.RELEASE
springVersion=4.1.7.RELEASE

View File

@@ -1,17 +1,18 @@
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method 'PUT'
url '/api/12'
headers {
header 'Content-Type': 'application/json'
}
body '''\
[{
"text": "Gonna see you at Warsaw"
}]
priority 2
request {
method 'PUT'
url '/api/12'
headers {
header 'Content-Type': 'application/json'
}
body '''\
[{
"text": "Gonna see you at Warsaw"
}]
'''
}
response {
status 200
}
}
response {
status 200
}
}

View File

@@ -1,21 +1,21 @@
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method 'PUT'
url $(client(regex('^/api/[0-9]{2}$')), server('/api/12'))
headers {
header 'Content-Type': 'application/json'
}
body '''\
[{
"text": "Gonna see you at Warsaw"
}]
request {
method 'PUT'
url $(client(regex('^/api/[0-9]{2}$')), server('/api/12'))
headers {
header 'Content-Type': 'application/json'
}
body '''\
[{
"text": "Gonna see you at Warsaw"
}]
'''
}
response {
body (
path: $(client('/api/12'), server(regex('^/api/[0-9]{2}$'))),
correlationId: $(client('1223456'), server(execute('isProperCorrelationId($it)')))
)
status 200
}
}
response {
body (
path: $(client('/api/12'), server(regex('^/api/[0-9]{2}$'))),
correlationId: $(client('1223456'), server(execute('isProperCorrelationId($it)')))
)
status 200
}
}

View File

@@ -16,21 +16,21 @@ import static org.springframework.web.bind.annotation.RequestMethod.PUT
@TypeChecked
class PairIdController {
@RequestMapping(
value = '{pairId}',
method = PUT,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
String getPlacesFromTweets(@PathVariable long pairId, @RequestBody List<com.ofg.twitter.place.Tweet> tweets) {
log.info("Inside PairIdController, doing very important logic")
if (tweets?.text != ["Gonna see you at Warsaw"]) {
throw new IllegalArgumentException("Wrong text in tweet: ${tweets?.text}")
}
return """
{
"path" : "/api/$pairId",
"correlationId" : 123456
}
"""
}
@RequestMapping(
value = '{pairId}',
method = PUT,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
String getPlacesFromTweets(@PathVariable long pairId, @RequestBody List<com.ofg.twitter.place.Tweet> tweets) {
log.info("Inside PairIdController, doing very important logic")
if (tweets?.text != ["Gonna see you at Warsaw"]) {
throw new IllegalArgumentException("Wrong text in tweet: ${tweets?.text}")
}
return """
{
"path" : "/api/$pairId",
"correlationId" : 123456
}
"""
}
}

View File

@@ -1,13 +1,13 @@
package com.ofg.twitter.place;
public class Tweet {
private String text;
private String text;
public String getText() {
return text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public void setText(String text) {
this.text = text;
}
}

View File

@@ -11,13 +11,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
class AcceptanceSpec extends Specification {
def "should have controller up and running"() {
given:
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new PairIdController()).build()
expect:
mockMvc.perform(put("/api/${1}").
contentType(MediaType.APPLICATION_JSON).
content("""[{"text":"Gonna see you at Warsaw"}]""")).
andExpect(status().isOk())
}
def "should have controller up and running"() {
given:
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new PairIdController()).build()
expect:
mockMvc.perform(put("/api/${1}").
contentType(MediaType.APPLICATION_JSON).
content("""[{"text":"Gonna see you at Warsaw"}]""")).
andExpect(status().isOk())
}
}

View File

@@ -6,11 +6,11 @@ import spock.lang.Specification
abstract class BaseMockMvcSpec extends Specification {
def setup() {
RestAssuredMockMvc.standaloneSetup(new PairIdController())
}
def setup() {
RestAssuredMockMvc.standaloneSetup(new PairIdController())
}
void isProperCorrelationId(Integer correlationId) {
assert correlationId == 123456
}
void isProperCorrelationId(Integer correlationId) {
assert correlationId == 123456
}
}

View File

@@ -0,0 +1,94 @@
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.2.6.RELEASE"
}
}
ext {
restAssuredVersion = '2.5.0'
spockVersion = '1.0-groovy-2.4'
wiremockVersion = '2.0.5-beta'
accurestStubsBaseDirectory = 'src/test/resources/stubs'
}
subprojects {
apply plugin: 'groovy'
repositories {
mavenCentral()
mavenLocal()
}
dependencies {
testCompile 'org.codehaus.groovy:groovy-all:2.4.5'
testCompile "org.spockframework:spock-core:$spockVersion"
testCompile 'junit:junit:4.12'
testCompile "com.github.tomakehurst:wiremock:$wiremockVersion"
}
}
configure([project(':fraudDetectionService'), project(':loanApplicationService')]) {
apply plugin: 'spring-boot'
apply plugin: 'accurest'
ext {
wireMockStubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/")
wireMockStubsOutputDir = new File(wireMockStubsOutputDirRoot, 'mappings/')
}
accurest {
targetFramework = 'Spock'
testMode = 'JaxRsClient'
baseClassForTests = 'com.blogspot.toomuchcoding.MvcSpec'
contractsDslDir = file("${project.projectDir.absolutePath}/mappings/")
generatedTestSourcesDir = file("${project.buildDir}/generated-sources/")
stubsOutputDir = wireMockStubsOutputDir
}
jar {
version = '0.0.1'
}
configurations {
compile.exclude module: "spring-boot-starter-tomcat"
}
dependencies {
compile 'org.glassfish.jersey.containers:jersey-container-jetty-http:2.15'
compile 'org.springframework.boot:spring-boot-starter-jersey'
compile 'org.springframework.boot:spring-boot-starter-jetty'
testRuntime "org.spockframework:spock-spring:$spockVersion"
compile 'org.glassfish.jersey.connectors:jersey-apache-connector:2.15'
testCompile 'org.springframework:spring-test'
}
task cleanup(type: Delete) {
delete 'src/test/resources/mappings', 'src/test/resources/stubs'
}
clean.dependsOn('cleanup')
}
configure(project(':fraudDetectionService')) {
test.dependsOn('generateWireMockClientStubs')
}
configure(project(':loanApplicationService')) {
task copyCollaboratorStubs(type: Copy) {
File fraudBuildDir = project(':fraudDetectionService').buildDir
from(new File(fraudBuildDir, "/production/${project(':fraudDetectionService').name}-stubs/"))
into "src/test/resources/"
}
generateAccurest.dependsOn('copyCollaboratorStubs')
}

View File

@@ -0,0 +1,27 @@
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method """PUT"""
url """/fraudcheck"""
body("""
{
"clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}",
"loanAmount":99999}
"""
)
headers {
header("""Content-Type""", """application/vnd.fraud.v1+json""")
}
}
response {
status 200
body( """{
"fraudCheckStatus": "${value(client('FRAUD'), server(regex('[A-Z]{5}')))}",
"rejectionReason": "Amount too high"
}""")
headers {
header('Content-Type': 'application/vnd.fraud.v1+json')
}
}
}

View File

@@ -0,0 +1,28 @@
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method 'PUT'
url '/fraudcheck'
body("""
{
"clientPesel":"${value(client(regex('[0-9]{10}')), server('1234567890'))}",
"loanAmount":123.123
}
"""
)
headers {
header('Content-Type', 'application/vnd.fraud.v1+json')
}
}
response {
status 200
body(
fraudCheckStatus: "OK",
rejectionReason: $(client(null), server(execute('assertThatRejectionReasonIsNull($it)')))
)
headers {
header('Content-Type': 'application/vnd.fraud.v1+json')
}
}
}

View File

@@ -0,0 +1,25 @@
package com.blogspot.toomuchcoding.frauddetection;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
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);
}
@Bean
ResourceConfig resourceConfig() {
return ResourceConfig.forApplication(new FraudRestApplication());
}
}

View File

@@ -0,0 +1,38 @@
package com.blogspot.toomuchcoding.frauddetection;
import com.blogspot.toomuchcoding.frauddetection.model.FraudCheck;
import com.blogspot.toomuchcoding.frauddetection.model.FraudCheckResult;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import javax.ws.rs.*;
import java.math.BigDecimal;
import static com.blogspot.toomuchcoding.frauddetection.model.FraudCheckStatus.FRAUD;
import static com.blogspot.toomuchcoding.frauddetection.model.FraudCheckStatus.OK;
@Controller
@Path("/")
public class FraudDetectionController {
private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json";
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");
@PUT
@Path("/fraudcheck")
@Produces(FRAUD_SERVICE_JSON_VERSION_1)
@Consumes(FRAUD_SERVICE_JSON_VERSION_1)
public FraudCheckResult fraudCheck(@RequestBody(required = false) FraudCheck fraudCheck) {
if (amountGreaterThanThreshold(fraudCheck)) {
return new FraudCheckResult(FRAUD, AMOUNT_TOO_HIGH);
}
return new FraudCheckResult(OK, NO_REASON);
}
private boolean amountGreaterThanThreshold(FraudCheck fraudCheck) {
return MAX_AMOUNT.compareTo(fraudCheck.getLoanAmount()) < 0;
}
}

View File

@@ -0,0 +1,13 @@
package com.blogspot.toomuchcoding.frauddetection;
import java.util.Collections;
import java.util.Set;
public class FraudRestApplication extends javax.ws.rs.core.Application {
@Override
public Set<Class<?>> getClasses() {
return Collections.<Class<?>>singleton(FraudDetectionController.class);
}
}

View File

@@ -0,0 +1,29 @@
package com.blogspot.toomuchcoding.frauddetection.model;
import java.math.BigDecimal;
public class FraudCheck {
private String clientPesel;
private BigDecimal loanAmount;
public FraudCheck() {
}
public String getClientPesel() {
return clientPesel;
}
public void setClientPesel(String clientPesel) {
this.clientPesel = clientPesel;
}
public BigDecimal getLoanAmount() {
return loanAmount;
}
public void setLoanAmount(BigDecimal loanAmount) {
this.loanAmount = loanAmount;
}
}

View File

@@ -0,0 +1,32 @@
package com.blogspot.toomuchcoding.frauddetection.model;
public class FraudCheckResult {
private FraudCheckStatus fraudCheckStatus;
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.blogspot.toomuchcoding.frauddetection.model;
public enum FraudCheckStatus {
OK, FRAUD
}

View File

@@ -0,0 +1,57 @@
package com.blogspot.toomuchcoding
import com.blogspot.toomuchcoding.frauddetection.Application
import com.blogspot.toomuchcoding.frauddetection.FraudRestApplication
import org.eclipse.jetty.server.Server
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider
import org.glassfish.jersey.client.ClientConfig
import org.glassfish.jersey.jetty.JettyHttpContainerFactory
import org.glassfish.jersey.server.ResourceConfig
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import spock.lang.Shared
import spock.lang.Specification
import javax.ws.rs.client.Client
import javax.ws.rs.client.ClientBuilder
import javax.ws.rs.client.WebTarget
import javax.ws.rs.core.UriBuilder
import static org.springframework.util.SocketUtils.findAvailableTcpPort
abstract class MvcSpec extends Specification {
@Shared
WebTarget webTarget
@Shared
private Server server
@Shared
private Client client
def setupSpec() {
URI baseUri = UriBuilder.fromUri("http://localhost").port(findAvailableTcpPort(8000)).build()
ResourceConfig resourceConfig = ResourceConfig.forApplication(new FraudRestApplication())
resourceConfig.property("contextConfig", new AnnotationConfigApplicationContext(Application))
server = JettyHttpContainerFactory.createServer(baseUri, resourceConfig, true)
ClientConfig clientConfig = new ClientConfig()
clientConfig.connectorProvider(new ApacheConnectorProvider())
client = ClientBuilder.newClient(clientConfig)
webTarget = client.target(baseUri)
server.start()
}
def cleanupSpec() {
client?.close()
server?.stop()
}
void assertThatRejectionReasonIsNull(def rejectionReason) {
assert !rejectionReason
}
}

View File

@@ -0,0 +1,6 @@
#Wed Jan 28 00:32:44 CET 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-2.4-all.zip

View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# 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
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# 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\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
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" ] ; 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"`
# 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
@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=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@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 Windowz 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

View File

@@ -0,0 +1,17 @@
package com.blogspot.toomuchcoding.frauddetection;
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,62 @@
package com.blogspot.toomuchcoding.frauddetection;
import com.blogspot.toomuchcoding.frauddetection.model.FraudCheckStatus;
import com.blogspot.toomuchcoding.frauddetection.model.FraudServiceRequest;
import com.blogspot.toomuchcoding.frauddetection.model.FraudServiceResponse;
import com.blogspot.toomuchcoding.frauddetection.model.LoanApplication;
import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationResult;
import com.blogspot.toomuchcoding.frauddetection.model.LoanApplicationStatus;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class LoanApplicationService {
private static final String FRAUD_SERVICE_JSON_VERSION_1 =
"application/vnd.fraud.v1+json";
private final RestTemplate restTemplate;
public LoanApplicationService() {
this.restTemplate = new RestTemplate();
}
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, FRAUD_SERVICE_JSON_VERSION_1);
ResponseEntity<FraudServiceResponse> response =
restTemplate.exchange("http://localhost:8080/fraudcheck", HttpMethod.PUT,
new HttpEntity<>(request, httpHeaders),
FraudServiceResponse.class);
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());
}
}

View File

@@ -0,0 +1,14 @@
package com.blogspot.toomuchcoding.frauddetection.model;
public class Client {
private String pesel;
public String getPesel() {
return pesel;
}
public void setPesel(String pesel) {
this.pesel = pesel;
}
}

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