Architecture pattern for "microservice" with hard logic (Spring boot) - java

i've got a microservice which implements some optimization function by calling many times another microservice (the second one calculates so called target function value and the first micriservice changes paramters of this tagrget function)
It leads to necessity of writing some logic in Rest Controller layer. To be clear some simplified code will be represented below
#RestController
public class OptimizerController {
private OptimizationService service;
private RestTemplate restTemplate;
#GetMapping("/run_opt")
public DailyOptResponse doOpt(){
Data iniData = service.prepareData(null);
Result r = restTemplate.postForObject(http://calc-service/plain_calc", iniData, Result.class);
double dt = service.assessResult(r);
while(dt > 0.1){
Data newData = service.preapreData(r);
r = restTemplate.postForObject(http://calc-service/plain_calc", newData , Result.class);
dt = service.assessResult(r);
}
return service.prepareResponce(r);
}
As i saw in examples all people are striving to keep rest controller as simple as possible and move all logic to service layer. But what if i have to call some other microservices from service layer? Should i keep logic of data formin in service layer and return it to controller layer, use RestTemplate object in service layer or something else?
Thank you for your help

It is straightforward.
The whole logic is in the service layer (including other services).
Simple example:
Controller:
#RestController
#RequestMapping("/api/users")
public class UserController {
private final UserManager userManager;
#Autowired
public UserController(UserManager userManager) {
super();
this.userManager = userManager;
}
#GetMapping()
public List<UserResource> getUsers() {
return userManager.getUsers();
}
#GetMapping("/{userId}")
public UserResource getUser(#PathVariable Integer userId) {
return userManager.getUser(userId);
}
#PutMapping
public void updateUser(#RequestBody UserResource resource) {
userManager.updateUser(resource);
}
}
Service:
#Service
public class UserManager {
private static final Logger log = LoggerFactory.getLogger(UserManager.class);
private final UserRepository userRepository;
private final UserResourceAssembler userResourceAssembler;
private final PictureManager pictureManager;
#Autowired
public UserManager(
UserRepository userRepository,
UserResourceAssembler userResourceAssembler,
PictureManager pictureManager
) {
super();
this.userRepository = userRepository;
this.userResourceAssembler = userResourceAssembler;
this.pictureManager= pictureManager;
}
public UserResource getUser(Integer userId) {
User user = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User with ID " + userId + " not found!"));
return userResourceAssembler.toResource(user);
}
public List<UserResource> getUsers() {
return userResourceAssembler.toResources(userRepository.findAll());
}
public void updateUser(UserResource resource) {
User user = userRepository.findById(resource.getId()).orElseThrow(() -> new NotFoundException("User with ID " + resource.getId() + " not found!"));
PictureResource pictureResource = pictureManager.savePicture(user);
user = userResourceAssembler.fromResource(user, resource);
user = userRepository.save(user);
log.debug("User {} updated.", user);
}
}
Service 2:
#Service
public class PictureManager {
private static final Logger log = LoggerFactory.getLogger(PictureManager.class);
private final RestTemplate restTemplate;
#Autowired
public PictureManager(RestTemplate restTemplate) {
super();
this.restTemplate = restTemplate;
}
public PictureResource savePicture(User user) {
//do some logic with user
ResponseEntity<PictureResource> response = restTemplate.exchange(
"url",
HttpMethod.POST,
requestEntity,
PictureResource.class);
return response.getBody();
}
}
Repository:
public interface UserRepository extends JpaRepository<User, Integer> {
User findByUsername(String username);
}

Related

Mocked method not wokring

This is the actual code
RateNegotiationController.java
#GetMapping(value = "/rate-negotiation/{uniqueId}", produces = {APPLICATION_JSON_VALUE})
public ResponseEntity<RateNegotiation> rateNegotiationByUniqueId(#PathVariable(name = "uniqueId") final String uniqueId) {
final RateNegotiation rateNegotiation =
rateNegotiationService.retrieveRateNegotiationsByUniqueId(uniqueId);
final Optional<String> courierID = validationUtils.getCourierIDFromToken();
if (courierID.isPresent()) {
if (!courierID.get().equals(rateNegotiation.getCourierId())) {
return ResponseEntity.notFound().build();
}
log.info("RateNegotiationController, rateNegotiationByUniqueId {} ", rateNegotiation);
return ResponseEntity.ok(rateNegotiation);
}
throw new CourierIdNotFoundException(COURIER_ID_NOT_FOUND);
}
ValidationUtils.java
public Optional<String> getCourierIDFromToken() {
if (appConfigBean.isSecurityEnabled()) {
return Optional.of(requestPayloadValidator.getCourierIDFromToken());
}
return Optional.empty();
}
I am writing the testcase for this one ..
#MockBean
private ValidationUtils validationUtils;
#MockBean
private AppConfigBean appConfigBean;
#MockBean
private RequestPayloadValidator requestPayloadValidator;
#Test
public void shouldRetrieveRateNegotiationDetailsByUniqueId(){
when(validationUtils.getCourierIDFromToken()).thenReturn(Optional.of("123456"));
when(appConfigBean.isSecurityEnabled()).thenReturn(true);
when(requestPayloadValidator.getCourierIDFromToken()).thenReturn("123456");
rateNegotiationServiceWireMockRule.stubFor(WireMock.get(urlEqualTo(RETRIEVE_RATE_NEGOTIATION_BY_UNIQUE_ID_PATH))
.willReturn(aResponse()
.withHeader(CONTENT_TYPE, APPLICATION_JSON_CHARSET)
.withBodyFile("RateNegotiationByUniqueId.json")
.withStatus(200)
)
);
given()
.port(port)
.when()
.header(CONTENT_TYPE, APPLICATION_JSON_CHARSET)
.get(RETRIEVE_RATE_NEGOTIATION_BY_UNIQUE_ID_URL)
.then()
.assertThat()
.statusCode(200);
}
but still it is not wokring and , showing error,CourierIdNotFoundException: Courier ID not found
I have mock the method validationUtils.getCourierIDFromToken() but still it is not wokring
can anyone please help ?
DemoAppController.java
package com.application.packagename.controller;
#RestController
#Api(value="demoappcontroller", description="Application configuration")
#RequestMapping("app")
#ApiIgnore
public class DemoAppController {
#Autowired
SomeService service;
#ApiOperation(value = "demo app config", response = DemoReponse.class)
#RequestMapping(value="/v1/getDemoAppInfo", produces = "application/json", method= RequestMethod.GET)
public ResponseEntity getDesc(#Valid DemoAppRequest demoAppRequest) {
DemoReponse response = service.getDemoAppInfo(demoAppRequest.getVarNameOne(),
demoAppRequest.getEnvType());
return new ResponseEntity(response, HttpStatus.OK);
}
}
DemoAppRequest.java
package com.application.packagename.model;
#Data
#Component("demoapprequestVo")
#ApiModel("demoapprequestVo")
public class DemoAppRequest {
#ApiModelProperty(example = "value1")
public String varNameOne;
#ApiModelProperty(example = "value2")
public String varNameTwo;
}
DemoAppControllerTest.java
public class DemoAppControllerTest extends TestServiceApiIntegerationTest {
private MultiValueMap<String, String> requestParams;
private URI url;
#BeforeEach
void init() {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("varNameOne", "value 1");
params.add("varNameTwo", "value 2");
requestParams = params;
url = URI.create("/app/v1/getDemoAppInfo/");
}
#Test
public void testGetDesc() throws Exception {
mockMvc.perform(get(url)
.params(requestParams))
.andExpect(status().isOk());
}
}
TestServiceApiIntegerationTest.java
#SpringBootTest
#AutoConfigureMockMvc
public class TestServiceApiIntegerationTest {
#Autowired
protected MockMvc mockMvc;
}
This is just a template for unit testing , you can follow and implement it in your project.

Optional and Java Spring Testing

I have a problem with my ControllerTest. I'm not sure how to test for the Optional - does someone know how? The other test gives me a NullPointerException for the stubbing : when(couponService.getCouponById(id)).thenReturn(expectedCoupon);
Would be awesome if someone could help me.
public class CouponControllerTest {
#MockBean
private CouponService couponService;
#MockBean
private UserService userService;
#Autowired
MockMvc mockMvc;
#Test
public void checkAndUpdateCoupon() throws Exception {
int id = 1;
int userId = 1;
Coupon expectedCoupon = new Coupon(1, 1);
when(couponService.getCouponById(id)).thenReturn(expectedCoupon);
List<User> userList = new ArrayList<User>();
when(userService.getAllUser()).thenReturn(userList);
List<Coupon> couponList = new ArrayList<Coupon>();
when(couponService.getAllCoupons()).thenReturn(couponList);
mockMvc.perform(get("/checkCoupon")
.param("id", "1")
.param("userId", "1"))
.andExpect(status().isOk())
.andExpect(view().name("couponPage"))
.andExpect(model().attribute("error", "Not correct user id or coupon id."))
.andExpect(model().attribute("users", userList))
.andExpect(model().attribute("coupons", couponList));
verify(couponService).updateCoupons(id, userId);
}
}
#Controller
public class CouponController {
#Autowired
CouponService couponService;
#Autowired
UserService userService;
#GetMapping("/checkCoupon")
public String checkCoupon(ModelMap model, #RequestParam Integer id, #RequestParam Integer userId, Coupon coupon) {
Optional<Coupon> couponFromDatabase = couponService.byUserIdAndId(coupon.getUserId(), coupon.getId());
if(couponFromDatabase.isEmpty()) {
String error = "Not correct user id or coupon id.";
model.addAttribute("error", error);
} else {
String message = couponService.updateCoupons(id, userId);
model.addAttribute("message", message);
}
List<User> userList = userService.getAllUser();
model.addAttribute("users", userList);
List<Coupon> couponList = couponService.getAllCoupons();
model.addAttribute("coupons", couponList);
return "couponPage";
}
}
I think you need to do some changes in mocking the first service.
when( couponService.byUserIdAndId(anyLong(), anyLong()) ).thenReturn( Optional.of(expectedCoupon) );
Here the anyLong() refer to any Incoming long data type number.
Override your existing code with this above line.

How to test #RestController when all of the mapping return with ResponseEntity?

I need to do unit testing on a #RestController where every method returns with a ResponseEntity.
I have a CRUD repository to use but I don't know how can I test it with the ResponseEntities.
#RestController
#RequestMapping("/events")
public class EventController {
#Autowired
private EventRepository eventRepository;
#GetMapping("")
public ResponseEntity<Iterable<Event>> getAll() {
return ResponseEntity.ok(eventRepository.findAll());
}
#GetMapping("/{id}")
public ResponseEntity<Event> get(#PathVariable Integer id) {
Optional<Event> event= eventRepository.findById(id);
if (event.isPresent()) {
return ResponseEntity.ok(event.get());
} else {
return ResponseEntity.notFound().build();
}
}
#PostMapping("")
public ResponseEntity<Event> post(#RequestBody Event event) {
EventsavedEvent = eventRepository.save(event);
return ResponseEntity.ok(savedEvent);
}
.
.
.
So far so good , I can help you .
First of all, you must add unit test dependency.
After that you must examine below code.
Below code only consist for create.
Good luck.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
#ActiveProfiles("dev")
public class EventControllerTests {
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void testCreateEvent() {
Event event = new Event(); // Your entity
event.setEventName("Test"); // Your entity attributes
URI location = testRestTemplate.postForLocation("http://localhost:8080/events", event);
Event event2 = testRestTemplate.getForObject(location, Event.class);
MatcherAssert.assertThat(event2.getEventName(), Matchers.equalTo(event.getEventName()));
}
}

How can I assert value created in void method?

I have class
public class CloneUserService {
private final UserRepository userRepository;
private final PersonRepository personRepository;
private final OrderRepository orderRepository;
public CloneUserService(UserRepository userRepository, PersonRepository personRepository, OrderRepository orderRepository) {
this.userRepository = userRepository;
this.personRepository = personRepository;
this.orderRepository = orderRepository;
}
public void createFromTemplate(String templateUserId) {
User templateUser = userRepository.getUserById(templateUserId);
Person templatePerson = personRepository.getPersonByUserId(templateUserId);
List<Order> templateOrders = orderRepository.getOrdersByUserId(templateUserId);
User newUser = cloneUserFromTemplate(templateUser);
newUser.setId("newId");
userRepository.save(newUser);
Person newPerson = clonePersonFromTemplate(templatePerson);
newPerson.setUser(newUser);
newPerson.setId("newId");
personRepository.save(newPerson);
for (Order templateOrder : templateOrders) {
Order newOrder = cloneOrderFromTemplate(templateOrder);
newOrder.setId("newId");
newOrder.setUSer(newUser);
orderRepository.save(newOrder);
}
}
private Order cloneOrderFromTemplate(Order templateOrder) {
//logic
return null;
}
private Person clonePersonFromTemplate(Person templatePerson) {
//logic
return null;
}
private User cloneUserFromTemplate(User templateUser) {
//logic
return null;
}
}
I need to test this method createFromTemplate.
I create this test. I create stabs for each repository and store saved object into this stub. And I add the additional method for getting this object for the assertion.
It works. But I have 2 problems:
My template object is mutable. It is not a big problem but it is a fact.
If I add new methods to repository interface I must implement it in stubs.
Mu question - How can I test cloned objects like theses from my example?
I don't use spring and H2DB or another in-memory database.
I have a MongoDB database.
If I use mockito I will not understand how to assert new objects in void method.
class CloneUserServiceTest {
private CloneUserService cloneUserService;
private UserRepositoryStub userRepository;
private PersonRepositoryStub personRepository;
private OrderRepositoryStub orderRepository;
#Before
public void setUp() {
User templateUser = new User();
Person templatePerson = new Person();
List<Order> templateOrders = Collections.singletonList(new Order());
userRepository = new UserRepositoryStub(templateUser);
personRepository = new PersonRepositoryStub(templatePerson);
orderRepository = new OrderRepositoryStub(templateOrders);
cloneUserService = new CloneUserService(userRepository, personRepository, orderRepository);
}
#Test
void createFromTemplate() {
cloneUserService.createFromTemplate("templateUserId");
User newUser = userRepository.getNewUser();
// assert newUser
Person newPerson = personRepository.getNewPerson();
// assert newPerson
Order newOrder = orderRepository.getNewOrder();
// assert newOrder
}
private static class UserRepositoryStub implements UserRepository {
private User templateUser;
private User newUser;
public UserRepositoryStub(User templateUser) {
this.templateUser = templateUser;
}
public User getUserById(String templateUserId) {
return templateUser;
}
public void save(User newUser) {
this.newUser = newUser;
}
public User getNewUser() {
return newUser;
}
}
private static class PersonRepositoryStub implements PersonRepository {
private Person templatePerson;
private Person newPerson;
public PersonRepositoryStub(Person templatePerson) {
this.templatePerson = templatePerson;
}
public Person getPersonByUserId(String templateUserId) {
return templatePerson;
}
public void save(Person newPerson) {
this.newPerson = newPerson;
}
public Person getNewPerson() {
return newPerson;
}
}
private static class OrderRepositoryStub implements OrderRepository {
private List<Order> templateOrders;
private Order newOrder;
public OrderRepositoryStub(List<Order> templateOrders) {
this.templateOrders = templateOrders;
}
public List<Order> getOrdersByUserId(String templateUserId) {
return templateOrders;
}
public void save(Order newOrder) {
this.newOrder = newOrder;
}
public Order getNewOrder() {
return newOrder;
}
}
}
In your scenario I would consider using mocking framework like Mockito.
Some main advantages:
Adding new methods to repository interface doesn't require implementing it in stubs
Supports exact-number-of-times and at-least-once verification
Allows flexible verification in order (e.g: verify in order what you want, not every single interaction)
Very nice and simple annotation syntax - #Mock, #InjectMocks, #Spy
Here is an example - maybe it will interest you:
// arrange
Warehouse mock = Mockito.mock(Warehouse.class);
//act
Order order = new Order(TALISKER, 50);
order.fill(warehouse); // fill will call remove() implicitly
// assert
Mockito.verify(warehouse).remove(TALISKER, 50); // verify that remove() method was actually called

Java 8/Spring constants in PreAuthorize annotation

In my Spring Boot project I have defined a following RestController method:
#PreAuthorize("hasAuthority('" + Permission.APPEND_DECISION + "')")
#RequestMapping(value = "/{decisionId}/decisions", method = RequestMethod.PUT)
public DecisionResponse appendDecisionToParent(#PathVariable #NotNull #DecimalMin("0") Long decisionId, #Valid #RequestBody AppendDecisionRequest decisionRequest) {
....
return new DecisionResponse(decision);
}
Right now in order to provide allowed authority name I use a following code construction:
#PreAuthorize("hasAuthority('" + Permission.APPEND_DECISION + "')")
where Permission.APPEND_DECISION is a constant:
public static final String APPEND_DECISION = "APPEND_DECISION";
Is there any more elegant way in Java/Spring in order to define such kind of code ?
Here is a simple approach to defining authorities in a single place that doesn't require any in-depth Spring Security config.
public class Authority {
public class Plan{
public static final String MANAGE = "hasAuthority('PLAN_MANAGE')";
public static final String APPROVE = "hasAuthority('PLAN_APPROVE')";
public static final String VIEW = "hasAuthority('PLAN_VIEW')";
}
}
Securing services...
public interface PlanApprovalService {
#PreAuthorize(Authority.Plan.APPROVE)
ApprovalInfo approvePlan(Long planId);
}
}
Thanks to oli37 I have implemented this logic in a following way:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
private DefaultMethodSecurityExpressionHandler defaultMethodExpressionHandler = new DefaultMethodSecurityExpressionHandler();
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return defaultMethodExpressionHandler;
}
public class DefaultMethodSecurityExpressionHandler extends org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler {
#Override
public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {
StandardEvaluationContext standardEvaluationContext = super.createEvaluationContextInternal(auth, mi);
((StandardTypeLocator) standardEvaluationContext.getTypeLocator()).registerImport(Permission.class.getPackage().getName());
return standardEvaluationContext;
}
}
}
#PreAuthorize("hasAuthority(T(Permission).APPEND_DECISION)")
#RequestMapping(value = "/{decisionId}/decisions", method = RequestMethod.PUT)
public DecisionResponse appendDecisionToParent(#PathVariable #NotNull #DecimalMin("0") Long decisionId, #Valid #RequestBody AppendDecisionRequest decisionRequest) {
...
return new DecisionResponse(decision);
}
I thing the good way is not to mixed both
You can have constants
public static final String ROLE_ADMIN = "auth_app_admin";
and have that other side
#PreAuthorize("hasRole(\"" + Constants.ROLE_ADMIN + "\")")
this is much clear

Categories