Skip to content
Snippets Groups Projects
Commit 2899202b authored by Maciej Szarlinski's avatar Maciej Szarlinski
Browse files

closes #10 Display pets' visits on owner details screen

parent f328e2da
No related branches found
No related tags found
No related merge requests found
Showing
with 220 additions and 40 deletions
......@@ -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>
......
......@@ -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();
}
}
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);
}
}
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());
}
}
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<>();
}
package org.springframework.samples.petclinic.api.application;
import lombok.Data;
/**
* @author Maciej Szarlinski
*/
@Data
public class PetType {
private final String name;
}
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;
}
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));
}
}
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())));
}
}
......@@ -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 {
......
......@@ -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 {
......
......@@ -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)
......
......@@ -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)
......
......@@ -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
......
......@@ -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>
......
......@@ -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;
});
}]);
......@@ -43,7 +43,6 @@
</td>
<td valign="top">
<table class="table-condensed">
<!-- TODO: no visits in pet resource -->
<thead>
<tr>
<th>Visit Date</th>
......
......@@ -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>
......
......@@ -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 {
......
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;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment