Check out the full series of Questions Management tutorial here.
Before building the API add new option for the Composite Option Service, there are a few things we have to do first:
Because the Composite Option Service will deal with questions and options, I will add model objects for each type to contain their information as follows:
Question:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.huongdanjava.optionservice.dto; import lombok.Data; @Data public class Question { private String id; private String description; private String categoryId; } |
Option:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.huongdanjava.optionservice.dto; import lombok.Data; @Data public class Option { private String id; private String description; private String note; private Boolean isCorrect; private String questionId; } |
Since the Composite Option Service will call the Core Question Service and Core Option Service, we will add their information in the application.properties file as follows:
1 2 |
corequestionservice.url=http://localhost:8081 coreoptionservice.url=http://localhost:8083 |
To handle the call to the Core Question Service, we will create an interface called CoreQuestionService:
1 2 3 4 5 6 7 |
package com.huongdanjava.optionservice.service; public interface CoreQuestionService { String getServiceUrl(); } |
with the implementation of CoreQuestionServiceImpl:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.huongdanjava.optionservice.service.impl; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.huongdanjava.optionservice.service.CoreQuestionService; @Service public class CoreQuestionServiceImpl implements CoreQuestionService { @Value("${corequestionservice.url}") private String coreQuestionServiceUrl; @Override public String getServiceUrl() { return coreQuestionServiceUrl; } } |
To handle the call to the Core Option Service, I will create an interface called CoreOptionService:
1 2 3 4 5 6 7 |
package com.huongdanjava.optionservice.service; public interface CoreOptionService { String getServiceUrl(); } |
with implementation is CoreOptionServiceImpl:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.huongdanjava.optionservice.service.impl; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.huongdanjava.optionservice.service.CoreOptionService; @Service public class CoreOptionServiceImpl implements CoreOptionService { @Value("${coreoptionservice.url}") private String coreOptionServiceUrl; @Override public String getServiceUrl() { return coreOptionServiceUrl; } } |
I will run this service using port 8183 so I will add the server.port property in the application.properties file as follows:
1 |
server.port=8183 |
OK, everything is ready, now I will go to the main part of this tutorial!
First, I will create a controller named CompositeOptionController with the following content:
1 2 3 4 5 6 7 8 9 10 |
package com.huongdanjava.optionservice; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/option") public class CompositeOptionController { } |
With this declaration, I also expose APIs for the Composite Option Service with the request URL starting with “/option”.
Next I will create a CompositeOptionService to handle all operations related to the Composite Option Service:
1 2 3 4 5 |
package com.huongdanjava.optionservice.service; public interface CompositeOptionService { } |
The first operation is add a new option.
To implement this operation, based on the option information need added, we will call the Core Question Service to see the question id that this option belongs to does exist or not. If it is existing, we will call Core Option Service to perform add new this option.
Specifically, I will add an abstract method to the CompositeOptionService class as follows:
1 2 3 4 5 6 7 8 9 10 11 |
package com.huongdanjava.optionservice.service; import com.huongdanjava.optionservice.dto.Option; import reactor.core.publisher.Mono; public interface CompositeOptionService { Mono<Option> addNewOption(Option option); } |
In order to implement the addNewOption() method above, we first need to call the Core Question Service to find the question base on the id of the question that the new Option object belongs to. By adding a new method in the CoreQuestionService:
1 |
Mono<Question> findById(String questionId); |
with the implementation of the CoreQuestionServiceImpl class is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 |
@Override public Mono<Question> findById(String questionId) { WebClient client = WebClient.builder() .baseUrl(getServiceUrl()) .build(); WebClient.ResponseSpec responseSpec = client.get() .uri("/question/" + questionId) .retrieve(); return responseSpec.bodyToMono(Question.class); } |
Next we will call to the Core Option Service to add a new option by adding a new method in the CoreOptionService class:
1 |
Mono<Option> addNewOption(Option option); |
with the implementation of the CoreOptionServiceImpl class is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 |
@Override public Mono<Option> addNewOption(Option option) { WebClient webClient = WebClient.builder() .baseUrl(getServiceUrl()) .build(); return webClient.post() .uri("/option/add") .body(BodyInserters.fromObject(option)) .retrieve() .bodyToMono(Option.class); } |
OK, now we will implement the addNewOption() method in our CompositeOptionService class.
We will inject CoreQuestionService and CoreOptionService service classes first:
1 2 3 4 5 |
@Autowired private CoreQuestionService coreQuestionService; @Autowired private CoreOptionService coreOptionService; |
In the addNewOption() method, we will first call the Core Question Service:
1 |
coreQuestionService.findById(option.getQuestionId()) |
If there is a question with the id that the object needs to add, the item will be returned. We will then call the Core Option Service to perform adding new option.
1 2 3 |
return coreQuestionService.findById(option.getQuestionId()) .flatMap(question -> coreOptionService.addNewOption(option)) .subscribeOn(Schedulers.elastic()); |
Calling the Core Option Service takes time to process, so I ran the process on another thread using the subscribeOn() method.
The last thing we need to do is to add a new method to the CompositeOptionController class to expose a POST request “/option/add”:
1 2 3 4 |
@PostMapping("/add") public Mono<ResponseEntity<Option>> addNewOption(@RequestBody Option option) { } |
then declare the CompositeOptionService object with @Autowired annotation:
1 2 |
@Autowired private CompositeOptionService compositeOptionService; |
to be able to use the addNewOption() method of the Composite Option Service we created earlier:
1 2 3 4 5 6 |
@PostMapping("/add") public Mono<ResponseEntity<Option>> addNewOption(@RequestBody Option option) { return compositeOptionService.addNewOption(option) .map(o -> ResponseEntity.ok(o)) .defaultIfEmpty(ResponseEntity.notFound().build()); } |
OK, here we have completed the API add new option for Composite Option Service . Let’s test it.
Currently I have some questions as follows:
Suppose I now need to add a new option to the question with id 5b697df545aac307e5c68be5:
The result will be: