From 2899202b4878e11d93b9013753057ddd6cbad210 Mon Sep 17 00:00:00 2001 From: Maciej Szarlinski <mszarlinski@gmail.com> Date: Thu, 24 Nov 2016 21:06:40 +0100 Subject: [PATCH] closes #10 Display pets' visits on owner details screen --- spring-petclinic-api-gateway/pom.xml | 6 ++- .../petclinic/api/ApiGatewayApplication.java | 15 +++++-- .../application/CustomersServiceClient.java | 20 +++++++++ .../api/application/OwnerDetails.java | 37 +++++++++++++++++ .../petclinic/api/application/PetDetails.java | 24 +++++++++++ .../petclinic/api/application/PetType.java | 12 ++++++ .../api/application/VisitDetails.java | 18 ++++++++ .../api/application/VisitsServiceClient.java | 33 +++++++++++++++ .../boundary/web/ApiGatewayController.java | 41 +++++++++++++++++++ .../customers/application/OwnerService.java | 2 +- .../customers/application/PetService.java | 2 +- .../boundary/web/pet/PetResourceTest.java | 2 +- .../petclinic/monitoring/Monitored.java | 2 +- .../monitoring/MonitoringConfig.java | 2 +- spring-petclinic-tracing-server/pom.xml | 4 +- .../owner-details/owner-details.controller.js | 2 +- .../owner-details/owner-details.template.html | 1 - spring-petclinic-vets-service/pom.xml | 4 ++ .../vets/application/VetService.java | 2 +- .../infrastructure/config/VetsProperties.java | 31 +++----------- .../vets/web/boundary/VetResourceTest.java | 2 +- .../visits/application/VisitService.java | 2 +- .../web/{visit => }/VisitResource.java | 2 +- 23 files changed, 223 insertions(+), 43 deletions(-) create mode 100644 spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/CustomersServiceClient.java create mode 100644 spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/OwnerDetails.java create mode 100644 spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/PetDetails.java create mode 100644 spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/PetType.java create mode 100644 spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitDetails.java create mode 100644 spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitsServiceClient.java create mode 100644 spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayController.java rename spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/boundary/web/{visit => }/VisitResource.java (99%) diff --git a/spring-petclinic-api-gateway/pom.xml b/spring-petclinic-api-gateway/pom.xml index 3a8cab7c..6c63f477 100644 --- a/spring-petclinic-api-gateway/pom.xml +++ b/spring-petclinic-api-gateway/pom.xml @@ -70,7 +70,11 @@ <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> - + <!-- Third parties --> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </dependency> </dependencies> <build> diff --git a/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java index ed03d994..984884e1 100644 --- a/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java +++ b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java @@ -3,14 +3,23 @@ package org.springframework.samples.petclinic.api; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; @EnableZuulProxy @EnableDiscoveryClient @SpringBootApplication public class ApiGatewayApplication { - public static void main(String[] args) { - SpringApplication.run(ApiGatewayApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(ApiGatewayApplication.class, args); + } + + @Bean + @LoadBalanced + RestTemplate loadBalancedRestTemplate() { + return new RestTemplate(); + } } diff --git a/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/CustomersServiceClient.java b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/CustomersServiceClient.java new file mode 100644 index 00000000..30bf8c8f --- /dev/null +++ b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/CustomersServiceClient.java @@ -0,0 +1,20 @@ +package org.springframework.samples.petclinic.api.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +/** + * @author Maciej Szarlinski + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CustomersServiceClient { + + private final RestTemplate loadBalancedRestTemplate; + + public OwnerDetails getOwner(final int ownerId) { + return loadBalancedRestTemplate.getForObject("http://customers-service/owners/{ownerId}", OwnerDetails.class, ownerId); + } +} diff --git a/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/OwnerDetails.java b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/OwnerDetails.java new file mode 100644 index 00000000..40357482 --- /dev/null +++ b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/OwnerDetails.java @@ -0,0 +1,37 @@ +package org.springframework.samples.petclinic.api.application; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.stream.Collectors.toList; + +/** + * @author Maciej Szarlinski + */ +@Data +public class OwnerDetails { + + private final int id; + + private final String firstName; + + private final String lastName; + + private final String address; + + private final String city; + + private final String telephone; + + private final List<PetDetails> pets = new ArrayList<>(); + + @JsonIgnore + public List<Integer> getPetIds() { + return pets.stream() + .map(PetDetails::getId) + .collect(toList()); + } +} diff --git a/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/PetDetails.java b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/PetDetails.java new file mode 100644 index 00000000..53c9055d --- /dev/null +++ b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/PetDetails.java @@ -0,0 +1,24 @@ +package org.springframework.samples.petclinic.api.application; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Maciej Szarlinski + */ +@Data +public class PetDetails { + + private final int id; + + private final String name; + + private final String birthDate; + + private final PetType type; + + private final List<VisitDetails> visits = new ArrayList<>(); + +} diff --git a/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/PetType.java b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/PetType.java new file mode 100644 index 00000000..fe956fc1 --- /dev/null +++ b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/PetType.java @@ -0,0 +1,12 @@ +package org.springframework.samples.petclinic.api.application; + +import lombok.Data; + +/** + * @author Maciej Szarlinski + */ +@Data +public class PetType { + + private final String name; +} diff --git a/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitDetails.java b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitDetails.java new file mode 100644 index 00000000..200e1c51 --- /dev/null +++ b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitDetails.java @@ -0,0 +1,18 @@ +package org.springframework.samples.petclinic.api.application; + +import lombok.Data; + +/** + * @author Maciej Szarlinski + */ +@Data +public class VisitDetails { + + private final int id; + + private final int petId; + + private final String date; + + private final String description; +} diff --git a/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitsServiceClient.java b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitsServiceClient.java new file mode 100644 index 00000000..2539a064 --- /dev/null +++ b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitsServiceClient.java @@ -0,0 +1,33 @@ +package org.springframework.samples.petclinic.api.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.groupingBy; + +/** + * @author Maciej Szarlinski + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class VisitsServiceClient { + + private final RestTemplate loadBalancedRestTemplate; + + public Map<Integer, List<VisitDetails>> getVisitsForPets(final List<Integer> petIds, final int ownerId) { + //TODO: expose batch interface in Visit Service + final ParameterizedTypeReference<List<VisitDetails>> responseType = new ParameterizedTypeReference<List<VisitDetails>>() { + }; + return petIds.parallelStream() + .flatMap(petId -> loadBalancedRestTemplate.exchange("http://visits-service/owners/{ownerId}/pets/{petId}/visits", HttpMethod.GET, null, + responseType, ownerId, petId).getBody().stream()) + .collect(groupingBy(VisitDetails::getPetId)); + } +} diff --git a/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayController.java b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayController.java new file mode 100644 index 00000000..02931fd0 --- /dev/null +++ b/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayController.java @@ -0,0 +1,41 @@ +package org.springframework.samples.petclinic.api.boundary.web; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.samples.petclinic.api.application.CustomersServiceClient; +import org.springframework.samples.petclinic.api.application.OwnerDetails; +import org.springframework.samples.petclinic.api.application.VisitDetails; +import org.springframework.samples.petclinic.api.application.VisitsServiceClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static java.util.Collections.emptyList; + +/** + * @author Maciej Szarlinski + */ +@RestController +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ApiGatewayController { + + private final CustomersServiceClient customersServiceClient; + + private final VisitsServiceClient visitsServiceClient; + + @GetMapping(value = "owners/{ownerId}") + public OwnerDetails getOwnerDetails(final @PathVariable int ownerId) { + final OwnerDetails owner = customersServiceClient.getOwner(ownerId); + supplyVisits(owner, visitsServiceClient.getVisitsForPets(owner.getPetIds(), ownerId)); + return owner; + } + + private void supplyVisits(final OwnerDetails owner, final Map<Integer, List<VisitDetails>> visitsMapping) { + owner.getPets().forEach(pet -> + pet.getVisits().addAll(Optional.ofNullable(visitsMapping.get(pet.getId())).orElse(emptyList()))); + } +} diff --git a/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/application/OwnerService.java b/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/application/OwnerService.java index f1505fda..ac363749 100644 --- a/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/application/OwnerService.java +++ b/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/application/OwnerService.java @@ -13,7 +13,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Collection; /** - * @author mszarlinski on 2016-10-30. + * @author Maciej Szarlinski */ @Service public class OwnerService { diff --git a/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/application/PetService.java b/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/application/PetService.java index b1e820f3..54d97ca9 100644 --- a/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/application/PetService.java +++ b/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/application/PetService.java @@ -14,7 +14,7 @@ import java.util.Collection; import java.util.Optional; /** - * @author mszarlinski on 2016-10-30. + * @author Maciej Szarlinski */ @Service public class PetService { diff --git a/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/boundary/web/pet/PetResourceTest.java b/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/boundary/web/pet/PetResourceTest.java index f8219d66..0151d114 100644 --- a/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/boundary/web/pet/PetResourceTest.java +++ b/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/boundary/web/pet/PetResourceTest.java @@ -20,7 +20,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; /** - * @author mszarlinski on 2016-11-05. + * @author Maciej Szarlinski */ @RunWith(SpringRunner.class) @WebMvcTest(PetResource.class) diff --git a/spring-petclinic-monitoring/src/main/java/org/springframework/samples/petclinic/monitoring/Monitored.java b/spring-petclinic-monitoring/src/main/java/org/springframework/samples/petclinic/monitoring/Monitored.java index ab760985..42e24a71 100644 --- a/spring-petclinic-monitoring/src/main/java/org/springframework/samples/petclinic/monitoring/Monitored.java +++ b/spring-petclinic-monitoring/src/main/java/org/springframework/samples/petclinic/monitoring/Monitored.java @@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * @author mszarlinski on 2016-11-09. + * @author Maciej Szarlinski */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) diff --git a/spring-petclinic-monitoring/src/main/java/org/springframework/samples/petclinic/monitoring/MonitoringConfig.java b/spring-petclinic-monitoring/src/main/java/org/springframework/samples/petclinic/monitoring/MonitoringConfig.java index 5fc83365..e322fd16 100644 --- a/spring-petclinic-monitoring/src/main/java/org/springframework/samples/petclinic/monitoring/MonitoringConfig.java +++ b/spring-petclinic-monitoring/src/main/java/org/springframework/samples/petclinic/monitoring/MonitoringConfig.java @@ -5,7 +5,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; /** - * @author mszarlinski on 2016-11-09. + * @author Maciej Szarlinski */ @Configuration @EnableAspectJAutoProxy diff --git a/spring-petclinic-tracing-server/pom.xml b/spring-petclinic-tracing-server/pom.xml index f6197cf9..f8dd04fb 100644 --- a/spring-petclinic-tracing-server/pom.xml +++ b/spring-petclinic-tracing-server/pom.xml @@ -4,8 +4,8 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> - <groupId>org.springframework.samples.petclinic.visits</groupId> - <artifactId>spring-petclinic-zipkin-server</artifactId> + <groupId>org.springframework.samples.petclinic.tracing</groupId> + <artifactId>spring-petclinic-tracing-server</artifactId> <version>1.4.2</version> <packaging>jar</packaging> <description>Zipkin server with UI</description> diff --git a/spring-petclinic-ui/src/scripts/owner-details/owner-details.controller.js b/spring-petclinic-ui/src/scripts/owner-details/owner-details.controller.js index 2dd8f633..d164378d 100644 --- a/spring-petclinic-ui/src/scripts/owner-details/owner-details.controller.js +++ b/spring-petclinic-ui/src/scripts/owner-details/owner-details.controller.js @@ -4,7 +4,7 @@ angular.module('ownerDetails') .controller('OwnerDetailsController', ['$http', '$stateParams', function ($http, $stateParams) { var self = this; - $http.get('api/customer/owners/' + $stateParams.ownerId).then(function (resp) { + $http.get('api/gateway/owners/' + $stateParams.ownerId).then(function (resp) { self.owner = resp.data; }); }]); diff --git a/spring-petclinic-ui/src/scripts/owner-details/owner-details.template.html b/spring-petclinic-ui/src/scripts/owner-details/owner-details.template.html index 7c93a518..ed2810f9 100644 --- a/spring-petclinic-ui/src/scripts/owner-details/owner-details.template.html +++ b/spring-petclinic-ui/src/scripts/owner-details/owner-details.template.html @@ -43,7 +43,6 @@ </td> <td valign="top"> <table class="table-condensed"> - <!-- TODO: no visits in pet resource --> <thead> <tr> <th>Visit Date</th> diff --git a/spring-petclinic-vets-service/pom.xml b/spring-petclinic-vets-service/pom.xml index b5c2c4c7..b8796ace 100644 --- a/spring-petclinic-vets-service/pom.xml +++ b/spring-petclinic-vets-service/pom.xml @@ -59,6 +59,10 @@ </dependency> <!-- Third parties--> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </dependency> <dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId> diff --git a/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/application/VetService.java b/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/application/VetService.java index 2227eab6..4dab7955 100644 --- a/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/application/VetService.java +++ b/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/application/VetService.java @@ -11,7 +11,7 @@ import javax.cache.annotation.CacheResult; import java.util.Collection; /** - * @author mszarlinski on 2016-10-30. + * @author Maciej Szarlinski */ @Service public class VetService { diff --git a/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/infrastructure/config/VetsProperties.java b/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/infrastructure/config/VetsProperties.java index 679de115..33df8f8f 100644 --- a/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/infrastructure/config/VetsProperties.java +++ b/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/infrastructure/config/VetsProperties.java @@ -1,46 +1,25 @@ package org.springframework.samples.petclinic.vets.infrastructure.config; +import lombok.Data; + import org.springframework.boot.context.properties.ConfigurationProperties; /** * Typesafe custom configuration. * - * @author mszarlinski on 2016-11-08. + * @author Maciej Szarlinski */ -//TODO: Lombok +@Data @ConfigurationProperties(prefix = "vets") public class VetsProperties { private Cache cache; + @Data public static class Cache { private int ttl; private int heapSize; - - public int getTtl() { - return ttl; - } - - public void setTtl(int ttl) { - this.ttl = ttl; - } - - public int getHeapSize() { - return heapSize; - } - - public void setHeapSize(int heapSize) { - this.heapSize = heapSize; - } - } - - public Cache getCache() { - return cache; - } - - public void setCache(Cache cache) { - this.cache = cache; } } diff --git a/spring-petclinic-vets-service/src/test/java/org/springframework/samples/petclinic/vets/web/boundary/VetResourceTest.java b/spring-petclinic-vets-service/src/test/java/org/springframework/samples/petclinic/vets/web/boundary/VetResourceTest.java index 957f8121..60172bf0 100644 --- a/spring-petclinic-vets-service/src/test/java/org/springframework/samples/petclinic/vets/web/boundary/VetResourceTest.java +++ b/spring-petclinic-vets-service/src/test/java/org/springframework/samples/petclinic/vets/web/boundary/VetResourceTest.java @@ -19,7 +19,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** - * @author mszarlinski on 2016-11-05. + * @author Maciej Szarlinski */ @RunWith(SpringRunner.class) @WebMvcTest(VetResource.class) diff --git a/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/application/VisitService.java b/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/application/VisitService.java index e0c06520..cdb06623 100644 --- a/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/application/VisitService.java +++ b/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/application/VisitService.java @@ -12,7 +12,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; /** - * @author mszarlinski on 2016-10-30. + * @author Maciej Szarlinski */ @Service public class VisitService { diff --git a/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/boundary/web/visit/VisitResource.java b/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/boundary/web/VisitResource.java similarity index 99% rename from spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/boundary/web/visit/VisitResource.java rename to spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/boundary/web/VisitResource.java index e67519ea..b0b9a7d1 100644 --- a/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/boundary/web/visit/VisitResource.java +++ b/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/boundary/web/VisitResource.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.samples.petclinic.visits.boundary.web.visit; +package org.springframework.samples.petclinic.visits.boundary.web; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -- GitLab