How to extract path variable in citrus simulator framework for multiple scenario? - spring

#Scenario("MyRestServiceScenario")
#RequestMapping(value = "/services/rest/simulator/{StudentId}", method = RequestMethod.GET)
public class MyRestServiceSimulator extends AbstractSimulatorScenario {
#Override
public void run(ScenarioDesigner scenario) {
scenario
.http()
.receive()
.get()
scenario.conditional().when(/*StudentId == something*/).actions(
scenario.http()
.send()
.response(HttpStatus.OK)
.payload(new ClassPathResource(template1.json))
);
scenario.conditional().when(/*StudentId == something*/).actions(
scenario.http()
.send()
.response(HttpStatus.NOT_FOUND)
.payload(new ClassPathResource(template2.json))
);
scenario.conditional().when(/*StudentId == something*/).actions(
scenario.http()
.send()
.response(HttpStatus.CREATED)
.payload(new ClassPathResource(template3.json))
);
}
}
On the basis of path variable of URI I need to return some responses. I am not getting any way to that. Same thing for Delete request. Is there any way I can fetch that variable or that URL so I can use that variable in scenario and return responses according to Path variable?

Related

Return mono object with response entity webflux

I have the below controller
#RestController
#RequestMapping("/view")
public class ViewController {
#GetMapping(value = "/{channelId}/**")
public Mono<ResponseEntity<ViewResponse>> viewObject(#PathVariable(value = "channelId") String channelId) {
return redisController.getChannelData(channelInfoset, channelId).map(response -> {
Mono<ViewResponse> processOutput = processViewUrl(channelId); // returns object of Mono.just
return new ResponseEntity<>(processOutput, responseHeaders, HttpStatus.OK);
}}
The method which returns the mono object
private Mono<ViewResponse> processViewUrl(String channelId){
return Mono.just(new ViewResponse("val","temp",false));
}
This gives me an error saying
Incompatible types. Found: 'reactor.core.publisher.Mono<java.lang.Object>', required: 'reactor.core.publisher.Mono<org.springframework.http.ResponseEntity<com.api.model.ViewResponse>>'
What is wrong over here?
processOutput is a Mono<ViewResponse>, not a ViewResponse. To obtain a Mono<ResponseEntity<ViewResponse>>, you should map processOutput to a ResponseEntity:
return redisController.getChannelData(channelInfoset, channelId)
.map(response -> {
Mono<ViewResponse> processOutput = processViewUrl(channelId);
return processOutput.map(value -> new ResponseEntity(value, response.getHeaders(), HttpStatus.OK));
}}
Note that if there's no constraint over your processViewUrl method signature (API compliance, potential other async/complex implementation, etc.), I would recommend to change it to return directly a ViewResponse instead of a mono. Mono.just is not necessary here.
In general cases, Mono.just is required only when you want to provide a value to an operation that requires a publisher as input.
If you return directly a ViewResponse instead of a Mono<ViewResponse>, your initial code should work.

Scatter & Gather using Spring Webclient

I am new to reactive programming concepts and trying to build one service that sends requests to a two backend service in parallel and combine those results.
Those two backend service has a different response structure and i have created a mapper method to convert all that into a common Response structure.
This is what i have right now and it is working when both the services return results.
public Mono<List<Response>> getRecords(String input){
List<Response> response = new ArrayList<>();
Mono<FirstApiResponse> gResp = this.firstWebClient.get().uri(uriBuilder -> uriBuilder
.path("/")
.queryParam("q", input)
.build()).retrieve()
.bodyToMono(FirstApiResponse.class).log()
.timeout(Duration.ofSeconds(50L));
Mono<SecondApiResponse> iResp = this.secondWebClient.get().uri(uriBuilder -> uriBuilder
.path("/search")
.queryParam("term", input)
.build()).retrieve()
.bodyToMono(SecondApiResponse.class).log().timeout(Duration.ofSeconds(50L));
return Mono.zip(firstResp,secResp).map(objects ->{
if(firstResp != null)
response.addAll(Mapper.convert(objects.getT1()));
if(secResp != null);
response.addAll(Mapper.convert(objects.getT2()));
return response;
});
}
public List<Response> convert(FirstApiResponse resp){
////
Mapping to Response object
////
return response;
}
public List<Response> convert(SecondApiResponse resp){
////
Mapping to Response object
////
return response;
}
I don't know if this is the right way to do it. Moreover, i want to make it in such a way that if there is any errors from any of this service, then it should still return the results from the other service. Right now it throws the exception and I am not able to figure out how to handle it properly
How to handle these errors in a proper way ?
This is a pretty valid scenario and there are many ways to handle it. One crude way would be to use onErrorReturn a new Model which you can handle. It could be either an empty response or a wrapper around your model whichever seems fit for your scenario.
Mono<Wrapper<FirstApiResponse>> gResp = this.firstWebClient.get().uri(uriBuilder -> uriBuilder
.path("/")
.queryParam("q", input)
.build()).retrieve()
.bodyToMono(FirstApiResponse.class).log()
.map( response -> new Wrapper().withResponse(response))
.timeout(Duration.ofSeconds(50L))
.doOnError(throwable -> logger.error("Failed", throwable))
.onErrorReturn(new Wrapper().withError( YourDefaultErrorReponse(...));
Mono<SecondApiResponse> iResp = this.secondWebClient.get().uri(uriBuilder -> uriBuilder
.path("/search")
.queryParam("term", input)
.build())
.retrieve()
.bodyToMono(SecondApiResponse.class).log()
.map( response -> new Wrapper().withResponse(response))
.timeout(Duration.ofSeconds(50L))
..doOnError(throwable -> logger.error("Failed", throwable))
.onErrorReturn(new Wrapper().withError( YourDefaultErrorReponse(...))
Again there are ways to return a default response. A simple one would be to use something like a wrapper
public final class Wrapper<T> {
private T response ;
private Error error;
public Wrapper<T> withResponse ( T response ){
this.response = response;
return this;
}
public Wrapper<T> withError( Error error) {
this.error = error;
return this;
}
public Boolean hasError(){
return error != null ;
}
public T getResponse(){
return response;
}
}

How to call a Put method from a Post method in the rest controller

i have 2 methods, the first one is a PUT method and the second one is a POST method and i want to call the first one in the second one but it doesn't work.
Here's my code :
PUT Method
#PutMapping("/classifs")
public ResponseEntity<Classif> updateClassif(#Valid #RequestBody Classif classif) throws URISyntaxException {
Classif result = classifRepository.save(classif);
return ResponseEntity.ok();
}
POST Method
#PostMapping(value = "/classifs/maj", headers = "accept=application/json")
public void tableauClassif(#RequestBody ClassificationHolder classificationHolder) {
for(ClassifLineHolder line : classificationHolder.getDetails()){
for(Classif c : line.getMapClassif().values()){
if(c.getId() != null && c.getId() !=0)
this.updateClassif(c); //this i what i tried to do but it does'nt work !!
else
classifRepository.save(c);
}
}
}

Difference between use #RequestMapping with and without method

I'm learning about Spring and MVC.
So, in the controller class, I have this method:
#RequestMapping(value="/buscaUsuario/{apodo}", method= RequestMethod.GET)
public String searchUser(#PathVariable("apodo") String apodo){
String res;
int usrId = this.usuarioService.bucarUsuario(apodo);
if(usrId == 0) res = "/error";
else res =("/user/"+Integer.toString(usrId));
return ("redirect:"+res);
}
And it works. But if I change it deleting the "method=RequestMethod.GET" part. I mean, using it like this:
#RequestMapping(value="/buscaUsuario/{apodo}")
public String searchUser(#PathVariable("apodo") String apodo){
String res;
int usrId = this.usuarioService.bucarUsuario(apodo);
if(usrId == 0) res = "/error";
else res =("/user/"+Integer.toString(usrId));
return ("redirect:"+res);
}
It ALSO works. So, my question is What's the difference?
The #RequestMapping annotation handles all types of incoming HTTP request including GET, POST, PUT etc. By default, it’s assumed all incoming requests to URLs are of the HTTP GET kind. To differentiate the mapping by HTTP request type, you need to explicitly specify the HTTP request method. for more information

Execute a simple call to a WebAPI Get using RestRequest and a single string parameter

I have the following code in my receiving controller:
[Route("api/StudentUserId/{string}"), HttpGet]
public StudentDto StudentUserId(string userId)
{
StudentModel sm = new StudentModel(userId);
StudentDto dto = sm.ConvertToDto();
return dto;
}
After running this project, I have another project that I use to test the WebAPI controller calls. I use the following code to read a student record form the database using their userId:
protected T SendRequestToReadItemUsingString<T>(string resource, string userId) where T : new()
{
string resourceString = string.Format("{0}/{{userId}}", resource);
RestRequest request = new RestRequest(resourceString, Method.GET);
request.AddUrlSegment("userId", userId);
RestClient restClient = new RestClient(Service.Location);
var response = restClient.Execute<T>(request);
T retVal = response.Data;
return retVal;
}
Comparible code seems to work if I change the userId to an int Id in both the controller and calling code. I can't seem to get it to work with string. If I place a breakpoint in the controller code it never hits it and the calling code just returns a null.
Thanks for your help
Please note that WebApi works based on reflection this means that your curly braces {vars} must match the same name in your methods.
Therefore to match this api/StudentUserId/{string} your method needs to be declare like this:
[Route("api/StudentUserId/{userId}"), HttpGet]
public StudentDto StudentUserId(string userId)
return userId;
}
Where the parameter {string} was replaced by userId.
If you want to read more about this Routing Rules here is similar post on this;
WebApi Routing Configuration

Resources