Spring/Jackson RequestParam with HashMap from Angular - java

Trying to understand what is wrong with the sending of a Hashmap of values.
Client-side (Angular)
var params = {
year: reportYear,
reportAgg: {
"interest": "Java",
"domain": "JavaCodeGeeks.com"
}
};
return $http.post('test/' + pathVariable1 + '/',
$.param(params), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
Server-Side (Spring/Jackson)
#RequestMapping(value = RqMapping, method = RequestMethod.POST)
public ResponseEntity sendReports(
final #PathVariable(value = "pathVariable1") String organizationId,
final #RequestParam(value = "year", required = true) int requestedYear,
final #RequestParam(value = "reportAgg", required = false) Map<String, String> reportAgg,
final HttpServletRequest request) {
In the request.getParametersMap its possible to see the expected reportAgg[interest] and reportAgg[domain] but still i get a null value for reportAgg in the mapping.

You can use JackSon's ObjectMapper to convert into HashMap
#RequestMapping(value = RqMapping, method = RequestMethod.POST)
public ResponseEntity sendReports(
final #PathVariable(value = "pathVariable1") String organizationId,
final #RequestParam(value = "year", required = true) int requestedYear,
final #RequestParam(value = "reportAgg", required = false) String reportAgg,
final HttpServletRequest request) {
ObjectMapper mapper = new ObjectMapper();
String json = reportAgg;
Map<String, String> map = new HashMap<String, String>();
// convert JSON string to Map
map = mapper.readValue(json, new TypeReference<Map<String, String>>(){});
System.out.println(map ); // out put as HashMap<String, String>
}// end of sendReports()

Related

Values are not being validated against openani-generated enum inside spring-boot project

I am using openapi 3.0.3 spec with openapi-generator-maven-plugin for Java code generation to build interfaces that are implemented in a spring-boot project.
/user/search:
get:
parameters:
- in: query
name: sortBy
description: Field to sort by
required: true
schema:
$ref: "#/components/schemas/UserSearchSortBy"
# omitting some code to shorted the copy-pasted spec
schemas:
UserSearchSortBy:
type: string
enum: [first_name, last_name, email, phone_number]
The above results in an interface that I can implement. The enum for UserSearchSortBy is created fine.
#RequestMapping(
method = RequestMethod.GET,
value = "/user/search",
produces = { "application/json" }
)
default ResponseEntity<UsersResponsePageableModel> _searchUsers(
#NotNull #Min(1) #Parameter(name = "currentPage", description = "Page number", required = true) #Valid #RequestParam(value = "currentPage", required = true) Integer currentPage,
#NotNull #Min(1) #Max(100) #Parameter(name = "pageSize", description = "Number of records to show per page", required = true) #Valid #RequestParam(value = "pageSize", required = true) Integer pageSize,
#NotNull #Parameter(name = "sortOrder", description = "Sort order", required = true) #Valid #RequestParam(value = "sortOrder", required = true) SortOrderEnumModel sortOrder,
#NotNull #Parameter(name = "sortBy", description = "Field to sort by", required = true) #Valid #RequestParam(value = "sortBy", required = true) UserSearchSortByModel sortBy,
#NotNull #Size(max = 128) #Parameter(name = "searchQuery", description = "Search field", required = true) #Valid #RequestParam(value = "searchQuery", required = true) String searchQuery
) {
return searchUsers(currentPage, pageSize, sortOrder, sortBy, searchQuery);
}
I expect the values that are being submitted to an API to be validated against the UserSearchSortBy enum. The issue is that there is no validation present. It looks like the generator is not generating a piece that is responsible for validating values against the enum. Any help is appreciated.
The following post helped me to create a converter factory.
#Component
public class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
private static class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim().toUpperCase());
}
}
#Override
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
}
And the following exception handler helps me craft a nice-looking error response.
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<?> handleMismatchException(MethodArgumentTypeMismatchException e) {
String message = e.getMessage();
Class<?> parameterType = e.getParameter().getParameterType();
if (parameterType.isEnum()) {
Object[] enumConstants = parameterType.getEnumConstants();
if (enumConstants != null && enumConstants.length > 0) {
if (e.getName() != null && !e.getName().isEmpty() && e.getValue() != null) {
message = String.format("Invalid value '%s' for field '%s'.", e.getValue(), e.getName()) + " Valid values are " + Arrays.asList(enumConstants);
}
}
}
Map<String, String> errors = new HashMap<>();
errors.put("message", message);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).header("Content-Type", MediaType.APPLICATION_JSON_VALUE).body(errors);
}

How to access data from RestTemplate as List

I want to access data from a spring boot service. The return type of the data is a List, but every time I access it, the list is empty.
This is my code:
Map<String, String> params = new HashMap<String, String>();
params.put("firstName", "test" );
params.put("lastName", "test1");
ResponseEntity<Person[]> response = restTemplate.getForEntity(url, Person[].class, params);
In this case, response.getBody() is an empty [].
#RequestMapping(value = "/search", method = RequestMethod.GET)
public List<Person> searchUsers(
#RequestParam(value = "firstName", required = false) String firstName,
#RequestParam(value = "lastName", required = false) String lastName,
#RequestParam(value = "email", required = false) String email {
return personService.search(firstName, lastName, email, company);
}
I also tried with String, and Person[], but nothing worked.
Thanks in advance!
#GET
#Path("statement")
#Produces({MediaType.APPLICATION_XML})
public Response statement(#QueryParam("from") String from, #QueryParam("to") String to) {
DB idb = new DB();
List<Transaction> transactions = idb.getTransactionsByDate(from, to);
final GenericEntity<List<Transaction>> entity = new GenericEntity<List<Transaction>>(transactions) {
};
return Response.status(Response.Status.OK).entity(entity).build();
}

Update a response object of the end-point

I generated automatically SpringMVC API using swagger. Now I want to update some end-points manually.
I have the folloiwng end-point:
#ApiOperation(value = "Estimation of ...", notes = "...", response = Similarity.class, responseContainer = "List")
#io.swagger.annotations.ApiResponses(value = {
#io.swagger.annotations.ApiResponse(code = 200, message = "Similarity metrics", response = Similarity.class),
#io.swagger.annotations.ApiResponse(code = 200, message = "Unexpected error", response = Similarity.class) })
#RequestMapping(value = "/estimateSimilarity",
produces = { "application/json" },
method = RequestMethod.GET)
public ResponseEntity<HashMap<String,Double>> estimateSimilarity(
#ApiParam(value = "...", required = true)
#RequestParam(value = "term1", required = true) String term,
#ApiParam(value = "...", required = true)
#RequestParam(value = "terms", required = true) List<String> concepts)
throws NotFoundException {
Similarity similarity = new Similarity();
HashMap<String,Double> result = similarity.getEstimates(term1, terms);
return new ResponseEntity<HashMap<String,Double>>(HttpStatus.OK);
}
Instead of response = Similarity.class, I want to return HashMap<String,Double> result. How should I update the above-given code to be able to return this object?
Try modifying the ApiOperations Response container.
#ApiOperation(value = "Estimation of ...", notes = "...", response = Double.class, responseContainer = "Map")

spring mvc requestmapping dynamic url

How do I change my request mapping for dynamic urls? URLs might look like this:
http://zz.zz.zz.com:8080/webapp/p1/q9/e3/test?Id=2&maxrows=5
http://zz.zz.zz.com:8080/webapp/a1/b2/c3/test?Id=2&maxrows=5
http://zz.zz.zz.com:8080/webapp/x1/y2/z3/test?Id=2&maxrows=5
Here's the working controller syntax when the url is in this format:
http://zz.zz.zz.com:8080/webapp/test?Id=2&maxrows=5
#RequestMapping(value = "/test", method = RequestMethod.GET)
public #ResponseBody void test(
#RequestParam(value = "Id", required = true) String Id,
#RequestParam(value = "maxrows", required = true) int maxrows
) throws Exception {
System.out.println("Id: " + Id + " maxrows: " + maxrows);
}
Try this:
#RequestMapping(value = "/test/{param1}/{param2}/{param3}")
public #ResponseBody void test(
#RequestParam(value = "Id", required = true) String Id,
#RequestParam(value = "maxrows", required = true) int maxrows,
#PathVariable(value = "param1") String param1,
#PathVariable(value = "param2") String param2,
#PathVariable(value = "param3") String param3) {
...
}
For more information look at Spring Reference Documentation

RestEasy: How to validate ClientResponse body?

I have a REST endpoint as
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getVariables(#QueryParam("_activeonly") #DefaultValue("no") #Nonnull final Active active) {
switch(active){
case yes:
return Response.ok(VariablePresentation.getPresentationVariables(variableManager.getActiveVariables())).build();
case no:
return Response.ok(VariablePresentation.getPresentationVariables(variableManager.getVariables())).build();
}
throw new WebApplicationException(Response.Status.BAD_REQUEST);
}
Which returns JSON of List of VariablePresentation. The VariablePresentaion looks like
#XmlRootElement
public class VariablePresentation {
private final UUID id;
private final String name;
private final VariableType type;
public VariablePresentation(#Nonnull final Variable variable) {
id = variable.getId();
name = variable.getName();
type = variable.getType();
}
public String getId() {
return id.toString();
}
#Nonnull
public String getName() {
return name;
}
#Nonnull
public VariableType getType() {
return type;
}
annotated with JAXB's XmlRoot to return JSON.
My integration test is as following
#Test
public void testGetAllVariablesWithoutQueryParamPass() throws Exception {
final ClientRequest clientCreateRequest = new ClientRequest("http://localhost:9090/variables");
final MultivaluedMap<String, String> formParameters = clientCreateRequest.getFormParameters();
final String name = "testGetAllVariablesWithoutQueryParamPass";
formParameters.putSingle("name", name);
formParameters.putSingle("type", "String");
formParameters.putSingle("units", "units");
formParameters.putSingle("description", "description");
formParameters.putSingle("core", "true");
final ClientResponse<String> clientCreateResponse = clientCreateRequest.post(String.class);
assertEquals(201, clientCreateResponse.getStatus());
}
I want to test the request body which returns the List<VariablePresentation> as String. How can I convert the response body (String) as VariablePresentation Object?
Update
After adding the following
final GenericType<List<VariablePresentation>> typeToken = new GenericType<List<VariablePresentation>>() {
};
final ClientResponse<List<VariablePresentation>> clientCreateResponse = clientCreateRequest.post(typeToken);
assertEquals(201, clientCreateResponse.getStatus());
final List<VariablePresentation> variables = clientCreateResponse.getEntity();
assertNotNull(variables);
assertEquals(1, variables.size());
Its fails with different Error
testGetAllVariablesWithoutQueryParamPass(com.myorg.project.market.integration.TestVariable): Unable to find a MessageBodyReader of content-type application/json and type java.util.List<com.myorg.project.service.presentation.VariablePresentation>
How do I resolve this?
The easiest way I found is to use ClientResponse with Gson
A simple example could be
#Test
public void testGetAllVariablesWithoutQueryParamNoPass() throws Exception {
final ClientRequest clientCreateRequest_var0 = new ClientRequest("http://localhost:9090/variables?_activeonly=no");
final MultivaluedMap<String, String> formParameters = clientCreateRequest_var0.getFormParameters();
final String name0 = "testGetAllVariablesWithoutQueryParamNoPass_0";
formParameters.putSingle("name", name0);
formParameters.putSingle("type", "String");
formParameters.putSingle("units", "units");
formParameters.putSingle("description", "description");
formParameters.putSingle("core", "true");
final ClientResponse<String> clientCreateResponse_var0 = clientCreateRequest_var0.post(String.class);
assertEquals(201, clientCreateResponse_var0.getStatus());
final ClientRequest clientCreateRequest_var1 = new ClientRequest("http://localhost:9090/variables");
final MultivaluedMap<String, String> formParameters1 = clientCreateRequest_var1.getFormParameters();
final String name1 = "testGetAllVariablesWithoutQueryParamNoPass_1";
formParameters1.putSingle("name", name1);
formParameters1.putSingle("type", "String");
formParameters1.putSingle("units", "units");
formParameters1.putSingle("description", "description");
formParameters1.putSingle("core", "true");
final ClientResponse<String> clientCreateResponse_var1 = clientCreateRequest_var1.post(String.class);
assertEquals(201, clientCreateResponse_var1.getStatus());
final ClientRequest clientCreateRequest2 = new ClientRequest("http://localhost:9090/variables");
final ClientResponse<String> clientCreateResponse2 = clientCreateRequest2.get(String.class);
final Gson gson = new Gson();
final Type variableType = new TypeToken<List<VariablePresentation>>() {
}.getType();
List<VariablePresentation> variables = gson.fromJson(clientCreateResponse2.getEntity(), variableType);
assertNotNull(variables);
assertEquals(2, variables.size());
// clean test data
removeTestDataVariableObject(name0);
removeTestDataVariableObject(name1);
}

Categories