From 3c90bce3ab6f292ba41123e544fbce45c0f9439f Mon Sep 17 00:00:00 2001 From: szimano Date: Sun, 14 Jun 2015 14:02:56 +0200 Subject: [PATCH 1/3] new, better api --- .../aggregatr/aggregation/shouldGetHop.groovy | 20 +++++++++++++++++++ .../aggregation/shouldGetIngredients.groovy | 5 ++++- .../aggregation/shouldGetMalt.groovy | 20 +++++++++++++++++++ .../aggregation/shouldGetWater.groovy | 20 +++++++++++++++++++ .../aggregation/shouldGetYiest.groovy | 20 +++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy create mode 100644 repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetMalt.groovy create mode 100644 repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetWater.groovy create mode 100644 repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetYiest.groovy diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy new file mode 100644 index 0000000..573d39a --- /dev/null +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy @@ -0,0 +1,20 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + request { + method 'POST' + url '/ingredients' + headers { + header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' + } + body( + items: ['MALT'] + ) + } + response { + status 200 + body( + ingredients: [ + [type: 'HOP', quantity: 200] + ] + ) + } +} \ No newline at end of file diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetIngredients.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetIngredients.groovy index c098d1b..42fd9f7 100644 --- a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetIngredients.groovy +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetIngredients.groovy @@ -1,10 +1,13 @@ io.codearte.accurest.dsl.GroovyDsl.make { request { - method 'GET' + method 'POST' url '/ingredients' headers { header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' } + body( + items: ['WATER', 'HOP', 'YIEST', 'MALT'] + ) } response { status 200 diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetMalt.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetMalt.groovy new file mode 100644 index 0000000..554483c --- /dev/null +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetMalt.groovy @@ -0,0 +1,20 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + request { + method 'POST' + url '/ingredients' + headers { + header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' + } + body( + items: ['MALT'] + ) + } + response { + status 200 + body( + ingredients: [ + [type: 'MALT', quantity: 200] + ] + ) + } +} \ No newline at end of file diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetWater.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetWater.groovy new file mode 100644 index 0000000..8477052 --- /dev/null +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetWater.groovy @@ -0,0 +1,20 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + request { + method 'POST' + url '/ingredients' + headers { + header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' + } + body( + items: ['WATER'] + ) + } + response { + status 200 + body( + ingredients: [ + [type: 'WATER', quantity: 200] + ] + ) + } +} \ No newline at end of file diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetYiest.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetYiest.groovy new file mode 100644 index 0000000..8df96eb --- /dev/null +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetYiest.groovy @@ -0,0 +1,20 @@ +io.codearte.accurest.dsl.GroovyDsl.make { + request { + method 'POST' + url '/ingredients' + headers { + header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' + } + body( + items: ['YIEST'] + ) + } + response { + status 200 + body( + ingredients: [ + [type: 'YIEST', quantity: 200] + ] + ) + } +} \ No newline at end of file From 1d63789f362e2e49dc7c7fddc26aa67a2d4a4551 Mon Sep 17 00:00:00 2001 From: szimano Date: Sun, 14 Jun 2015 15:38:02 +0200 Subject: [PATCH 2/3] hop should be hop --- .../pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy index 573d39a..01abf51 100644 --- a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy @@ -6,7 +6,7 @@ io.codearte.accurest.dsl.GroovyDsl.make { header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' } body( - items: ['MALT'] + items: ['HOP'] ) } response { From dada05e1b21a7495785d4ca2a2fca7ddb05d9e6f Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 15 Jun 2015 01:36:43 +0200 Subject: [PATCH 3/3] Updated functionality and tests according to the contract --- .../aggregatr/aggregation/shouldGetHop.groovy | 6 +++--- .../aggregation/shouldGetIngredients.groovy | 12 +++++------ .../aggregation/shouldGetMalt.groovy | 6 +++--- .../aggregation/shouldGetWater.groovy | 6 +++--- .../aggregation/shouldGetYiest.groovy | 6 +++--- .../aggregation/IngredientsAggregator.java | 11 +++++----- .../aggregation/IngredientsController.java | 11 ++++++---- .../aggregation/IngredientsProperties.java | 21 +++++++++++++++---- .../aggregatr/aggregation/model/Order.java | 11 ++++++++++ .../aggregatr/aggregation/model/Version.java | 5 +++++ .../acceptance/AcceptanceSpec.groovy | 10 ++++++--- .../aggregation/BaseMockMvcSpec.groovy | 13 ++++++------ src/test/resources/application-test.yaml | 5 +---- 13 files changed, 78 insertions(+), 45 deletions(-) create mode 100644 src/main/java/pl/devoxx/aggregatr/aggregation/model/Order.java create mode 100644 src/main/java/pl/devoxx/aggregatr/aggregation/model/Version.java diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy index 01abf51..976c96d 100644 --- a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetHop.groovy @@ -5,9 +5,9 @@ io.codearte.accurest.dsl.GroovyDsl.make { headers { header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' } - body( - items: ['HOP'] - ) + body(''' + { "items" : ["HOP"] } + ''') } response { status 200 diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetIngredients.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetIngredients.groovy index 42fd9f7..f1330eb 100644 --- a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetIngredients.groovy +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetIngredients.groovy @@ -5,18 +5,18 @@ io.codearte.accurest.dsl.GroovyDsl.make { headers { header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' } - body( - items: ['WATER', 'HOP', 'YIEST', 'MALT'] - ) + body(''' + { "items" : ["MALT","WATER","HOP","YIEST"] } + ''') } response { status 200 body( ingredients: [ - [type: 'MALT', quantity: 100], + [type: 'MALT', quantity: 200], [type: 'WATER', quantity: 200], - [type: 'HOP', quantity: 300], - [type: 'YIEST', quantity: 400] + [type: 'HOP', quantity: 200], + [type: 'YIEST', quantity: 200] ] ) } diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetMalt.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetMalt.groovy index 554483c..86a5422 100644 --- a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetMalt.groovy +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetMalt.groovy @@ -5,9 +5,9 @@ io.codearte.accurest.dsl.GroovyDsl.make { headers { header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' } - body( - items: ['MALT'] - ) + body(''' + { "items" : ["MALT"] } + ''') } response { status 200 diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetWater.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetWater.groovy index 8477052..d8eab64 100644 --- a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetWater.groovy +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetWater.groovy @@ -5,9 +5,9 @@ io.codearte.accurest.dsl.GroovyDsl.make { headers { header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' } - body( - items: ['WATER'] - ) + body(''' + { "items" : ["WATER"] } + ''') } response { status 200 diff --git a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetYiest.groovy b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetYiest.groovy index 8df96eb..0799b6d 100644 --- a/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetYiest.groovy +++ b/repository/mappings/pl/devoxx/aggregatr/aggregation/shouldGetYiest.groovy @@ -5,9 +5,9 @@ io.codearte.accurest.dsl.GroovyDsl.make { headers { header 'Content-Type': 'application/vnd.pl.devoxx.aggregatr.v1+json' } - body( - items: ['YIEST'] - ) + body(''' + { "items" : ["YIEST"] } + ''') } response { status 200 diff --git a/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsAggregator.java b/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsAggregator.java index 1e35a8c..0caf6dc 100644 --- a/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsAggregator.java +++ b/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsAggregator.java @@ -14,6 +14,7 @@ import pl.devoxx.aggregatr.aggregation.model.Ingredient; import pl.devoxx.aggregatr.aggregation.model.IngredientType; import pl.devoxx.aggregatr.aggregation.model.Ingredients; +import pl.devoxx.aggregatr.aggregation.model.Order; import java.lang.invoke.MethodHandles; import java.util.Arrays; @@ -51,14 +52,14 @@ private String getMetricName(IngredientType ingredientType) { return "ingredients." + ingredientType.toString().toLowerCase(); } - Ingredients fetchIngredients() { - final List> futures = ingredientsProperties - .getListOfServiceNames() + Ingredients fetchIngredients(Order order) { + List> futures = ingredientsProperties + .getListOfServiceNames(order) .stream() .map(ingredientHarvester::harvest) .collect(Collectors.toList()); - final ListenableFuture> allDoneFuture = Futures.allAsList(futures); - final List allIngredients = Futures.getUnchecked(allDoneFuture); + ListenableFuture> allDoneFuture = Futures.allAsList(futures); + List allIngredients = Futures.getUnchecked(allDoneFuture); allIngredients.forEach(this::updateIngredientCache); return getIngredientsStatus(); } diff --git a/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsController.java b/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsController.java index 9602c99..1e92578 100644 --- a/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsController.java +++ b/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsController.java @@ -2,13 +2,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import pl.devoxx.aggregatr.aggregation.model.Ingredients; +import pl.devoxx.aggregatr.aggregation.model.Order; +import pl.devoxx.aggregatr.aggregation.model.Version; @RestController -@RequestMapping("/ingredients") +@RequestMapping(value = "/ingredients", consumes = Version.V1, produces = MediaType.APPLICATION_JSON_VALUE) public class IngredientsController { private final IngredientsAggregator ingredientsAggregator; @@ -18,9 +21,9 @@ public IngredientsController(IngredientsAggregator ingredientsAggregator) { this.ingredientsAggregator = ingredientsAggregator; } - @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) - public Ingredients distributeIngredients() { - return ingredientsAggregator.fetchIngredients(); + @RequestMapping(method = RequestMethod.POST) + public Ingredients distributeIngredients(@RequestBody Order order) { + return ingredientsAggregator.fetchIngredients(order); } } diff --git a/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsProperties.java b/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsProperties.java index 87d7721..825845f 100644 --- a/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsProperties.java +++ b/src/main/java/pl/devoxx/aggregatr/aggregation/IngredientsProperties.java @@ -1,20 +1,33 @@ package pl.devoxx.aggregatr.aggregation; -import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableMap; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; +import pl.devoxx.aggregatr.aggregation.model.IngredientType; +import pl.devoxx.aggregatr.aggregation.model.Order; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @ConfigurationProperties("ingredients") @Data public class IngredientsProperties { - private String serviceNames = "chmieleo,slodeo,wodeo,drozdzeo"; + private Map serviceNames = ImmutableMap.builder() + .put(IngredientType.WATER, "wodeo") + .put(IngredientType.MALT, "slodeo") + .put(IngredientType.HOP, "chmieleo") + .put(IngredientType.YIEST, "drozdzeo") + .build(); private String rootUrl = "http://localhost:8030/"; private Integer threshold = 1000; - public List getListOfServiceNames() { - return Splitter.on(',').omitEmptyStrings().trimResults().splitToList(serviceNames); + public List getListOfServiceNames(Order order) { + return serviceNames.entrySet() + .stream() + .filter((entry -> order.getItems().contains(entry.getKey()))) + .map((Map.Entry::getValue)) + .collect(Collectors.toList()); } } diff --git a/src/main/java/pl/devoxx/aggregatr/aggregation/model/Order.java b/src/main/java/pl/devoxx/aggregatr/aggregation/model/Order.java new file mode 100644 index 0000000..448b375 --- /dev/null +++ b/src/main/java/pl/devoxx/aggregatr/aggregation/model/Order.java @@ -0,0 +1,11 @@ +package pl.devoxx.aggregatr.aggregation.model; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class Order { + private List items = new ArrayList<>(); +} diff --git a/src/main/java/pl/devoxx/aggregatr/aggregation/model/Version.java b/src/main/java/pl/devoxx/aggregatr/aggregation/model/Version.java new file mode 100644 index 0000000..398f79f --- /dev/null +++ b/src/main/java/pl/devoxx/aggregatr/aggregation/model/Version.java @@ -0,0 +1,5 @@ +package pl.devoxx.aggregatr.aggregation.model; + +public class Version { + public static final String V1 = "application/vnd.pl.devoxx.aggregatr.v1+json"; +} diff --git a/src/test/groovy/pl/devoxx/aggregatr/acceptance/AcceptanceSpec.groovy b/src/test/groovy/pl/devoxx/aggregatr/acceptance/AcceptanceSpec.groovy index b371e2b..330aa1b 100644 --- a/src/test/groovy/pl/devoxx/aggregatr/acceptance/AcceptanceSpec.groovy +++ b/src/test/groovy/pl/devoxx/aggregatr/acceptance/AcceptanceSpec.groovy @@ -1,12 +1,12 @@ package pl.devoxx.aggregatr.acceptance - import groovy.json.JsonSlurper import org.springframework.test.context.ContextConfiguration import org.springframework.test.web.servlet.MvcResult +import pl.devoxx.aggregatr.aggregation.model.Version import pl.devoxx.aggregatr.base.MicroserviceMvcWiremockSpec import static java.net.URI.create -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print @ContextConfiguration @@ -20,7 +20,11 @@ class AcceptanceSpec extends MicroserviceMvcWiremockSpec { } private MvcResult getting_ingredients() { - return mockMvc.perform(get(create('/ingredients'))).andDo(print()).andReturn() + return mockMvc.perform(post(create('/ingredients')) + .header('Content-Type', Version.V1) + .content('{"items":["WATER","HOP","YIEST","MALT"]}')) + .andDo(print()) + .andReturn() } private void aggregated_ingredients_are_present(MvcResult result) { diff --git a/src/test/groovy/pl/devoxx/aggregatr/aggregation/BaseMockMvcSpec.groovy b/src/test/groovy/pl/devoxx/aggregatr/aggregation/BaseMockMvcSpec.groovy index c61ee90..fadc6b3 100644 --- a/src/test/groovy/pl/devoxx/aggregatr/aggregation/BaseMockMvcSpec.groovy +++ b/src/test/groovy/pl/devoxx/aggregatr/aggregation/BaseMockMvcSpec.groovy @@ -1,12 +1,14 @@ package pl.devoxx.aggregatr.aggregation import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc import pl.devoxx.aggregatr.aggregation.model.Ingredient -import pl.devoxx.aggregatr.aggregation.model.IngredientType import pl.devoxx.aggregatr.aggregation.model.Ingredients +import pl.devoxx.aggregatr.aggregation.model.Order import spock.lang.Specification abstract class BaseMockMvcSpec extends Specification { + protected static final int QUANTITY = 200 + IngredientsAggregator ingredientsAggregator = Stub() def setup() { @@ -15,12 +17,9 @@ abstract class BaseMockMvcSpec extends Specification { } void setupMocks() { - ingredientsAggregator.fetchIngredients() >> new Ingredients([ - new Ingredient(IngredientType.MALT, 100), - new Ingredient(IngredientType.WATER, 200), - new Ingredient(IngredientType.HOP, 300), - new Ingredient(IngredientType.YIEST, 400) - ]) + ingredientsAggregator.fetchIngredients(_) >> { Order order -> + return new Ingredients(order.items.collect { new Ingredient(it, QUANTITY)}) + } } } diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml index db6dcd9..4889dc4 100644 --- a/src/test/resources/application-test.yaml +++ b/src/test/resources/application-test.yaml @@ -5,7 +5,4 @@ stubrunner.stubs: group: com.ofg module: stub-runner-examples -stubrunner.work-offline: true - -ingredients: - serviceNames: chmieleo,slodeo,wodeo,drozdzeo \ No newline at end of file +stubrunner.work-offline: true \ No newline at end of file