Show a Model of an Enum in swagger ui - spring

Preface:
I use a MultiValueMap<String, String> in order to get the parameters and i want that the keys of the map are part of an Enum.
I cannot do MultiValueMap<Enum, String> since I asked (https://github.com/spring-projects/spring-framework/issues/28288) and by design it should be a String (otherwise strange things happen).
The problem:
I am having a problem trying to document the enum that I use as key of the MultiValueMap parameter for my post request.
I tried
#ApiModel(value = "...", description = "...")
but it does not show the Model.
I found that in order to document a class I should add
.additionalModels(typeResolver.resolve(MyEnum.class))
and I noticed that it works for another class but it does not work this Enum (the other class is not an enum).
Now I don't really know what I should try. Any suggests?
Thank you.

Related

In spring data elasticsearch, how can I project or add a calculated field on all results?

I am using spring-data-elasticsearch (latest version) along with a docker instance of elasticsearch (latest version), and I want to calculate a field on all results that are returned from my repository after a query. I do not want this information in the repository, because it is sometimes query dependent, and sometimes environment dependent. For example, if we perform a query, I want to generate a URL that includes the query terms as query parameters in the URL that I want to enrich the result with. There are some other cases, too. I have tried creating a spring data custom reading converter that accepts the whole document object. I can see that it is recognized when the application starts, but it is never invoked. How can I either project a field with a custom value, or enrich the returned documents with a contextually calculated value?
I first thought about AfterConvertCallback as well like Chin commented, but in a callback you have no context of the query that was run to get the entity, so you cannot use things like query terms to build something.
I would add the property - let's name it url of type String here - to the entity and mark it with the org.springframework.data.annotation.Transient annotation to prevent it from being stored.
Then in the method where you do the search, either using ElasticsearchOperations or a repository, postprocess the returned entites (code not tested, just written down here):
SearchHits<Entity> searchHits = repository.findByFoo(String fooValue);
searchHits.getSearchHits().forEach(searchHit -> {
searchHit.getContent().setUrl(someValueDerivedFromEnvironemtAndQuery);
});
After that proceed using the SearchHits.
I like a hybrid approach of combining the answers from both #ChinHuang and #PJMeisch. Both answers have their applicability, depending on the context or situation. I like Chin Huang's suggestion for instance-based information, where you would need things like configuration values. I also agree that PJ Meisch is correct in his concern that this does not give you access to the immediate query, so I like his idea of intercepting/mapping the values when the data is being returned from the data store. I appreciate the great information from both people, because this combination of both approaches is a solution that I am happy with.
I prefer to use a repository interface wherever possible, because many people incorrectly mix business logic into their repositories. If I want custom implementation, then I am forced to really think about it, because I have to create an "Impl" class to achieve it. This is not the gravest of errors, but I always accompany a repository with a business service that is responsible for any data grooming, or any programmatic action that is not strictly retrieval, or persistence, of data.
Here is the part of my module configuration where I create the custom AfterConvertCallback. I set the base URL in the onAfterConvert method:
#Bean
AfterConvertCallback<BookInfo> bookInfoAfterConvertCallback() {
return new BookInfoAfterConvertCallback(documentUrl);
}
static class BookInfoAfterConvertCallback implements AfterConvertCallback<BookInfo> {
private final String documentUrl;
public BookInfoAfterConvertCallback(String documentUrl) {
this.documentUrl = documentUrl;
}
#Override
public BookInfo onAfterConvert(final BookInfo entity, final Document document, final IndexCoordinates indexCoordinates) {
entity.setUrl(String.format("%s?id=%d", documentUrl, entity.getId()));
return entity;
}
}
In the data service that invokes the repository query, I wrote a pair of functions that creates the query param portion of the URL so that I can append it in any applicable method that uses the auto-wired repository instance:
/**
* Given a term, encode it so that it can be used as a query parameter in a URL
*/
private static final Function<String, String> encodeTerm = term -> {
try {
return URLEncoder.encode(term, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
log.warn("Could not encode search term for document URL", e);
return null;
}
};
/**
* Given a list of search terms, transform them into encoded URL query parameters and append
* them to the given URL.
*/
private static final BiFunction<List<String>, String, String> addEncodedUrlQueryParams = (searchTerms, url) ->
searchTerms.stream()
.map(term -> String.format("term=%s", encodeTerm.apply(term)))
.filter(Objects::nonNull)
.collect(Collectors.joining("&", url + "&", ""));
This absolutely can all be done in a repository instance, or in its enclosing service. But, when you want to intercept all data that is retrieved, and do something with it that is not specific to the query, then the callback is a great option because it does not incur the maintenance cost of needing to introduce it in every data layer method where it should apply. At query time, when you need to reference information that is only available in the query, it is clearly a matter of introducing this type of code into your data layer (service or repo) methods.
I am adding this as an answer because, even though I didn't realize it at the time that I posted my question, this is two concerns that are separate enough to warrant both approaches. I do not want to claim credit for this answer, so I will not select it as the answer unless you both comment on this, and tell me that you want me to do that.

Best practice of creation GET methods with many parameters(filters)

I have the GET method in my Spring REST controller. This method returns the list of users by the filter.
I have a few ways to implement it:
Add #PathVariable like - /users/{type}/{age}/{name}/...(bad approach in this case)
Add #RequestParam like - /users?type=type,age=age,name=name...(usual approach in this case)
Use RequestDto (the best approach) like
public class UsersRequestDto {
private String type;
private int age;
private String name;
...
}
But I can not use GET method for this. I must use POST method with #RequestBody
And it breaks the rules. My method doesn't change state and doesn't create any entities. It workes as the GET method but in reality, it is POST.
And I have 2 ways:
Use the GET method with many parameters
Use the POST method with DTO which works as the GET method and confuses users.
Which way is better?
Short version: you might be looking for How to bind #RequestParam to object in Spring. (See also: https://stackoverflow.com/a/16942352/54734 )
On the web, we would have an html form with a GET action. When the form is submitted, the browser would process the input controls and create the application/x-www-form-urlencoded representation of the form data. For a GET action, that representation is used as the query string.
Using GET, and encoding all of the information into the query string, allows us to take advantage of general purpose caching of the results.
But the query parameters aren't accessible by themselves - they are actually embedded within the larger context of the HTTP request. We don't usually see that because, once again, general purpose components can do a lot of the heavy lifting.
So we don't see the parser that extracts the target-uri from the request, or the parser that splits the target URI into its separate components, or the parser that splits the query part into a sequence of key value pairs....
In general, what we do is ride the "general purpose" implementation as far as we can, then get off and do the rest of the work ourselves. If the framework offered no better support for object mapping, that could mean implementing that mapping ourselves.
So if our framework lacked the capability to map the query string directly to an object representation, we would hand roll that part of the implementation ourselves (either by copying each parameter "by hand", or writing our own reflection code to do the mapping automagically).
But it seems that Spring has that capability already built into it; and that it is the default option (no annotation required); you just have to be sure that the object implementation provides the interface that Spring needs to execute the mapping.
How many different parameters are you including in your query?
Personally, I prefer the option of a GET method with many different parameters. It has other benefits such as being cacheable as well. Also, compare it to something like a the URL that a Google search generates - lots of query string parameters.
The POST option feels dirty - it's a violation of what a POST should actually do (creating or updating a resource).
See these discussions: https://softwareengineering.stackexchange.com/questions/233164/how-do-searches-fit-into-a-restful-interface and REST API Best practices: Where to put parameters?
1st of all when you are using RequestParam then key will be added with & symbol not with comma(,) .
when you want to filter ( as you have mentioned) something then best approach would be to use RequestParam.
To minimize the code you can opt to "MultiValueMap" or "HttpservletRequest" .
1)HttpServletRequest
#GetMapping("/user")
public List<User> getFilteredUser(HttpServletRequest httpservlet)
httpservlet.getQuesryString() //will return all request param.
2)MultiValueMap
#RequestParam MultiValueMap<String,String> params
NOTE:- Generally POST is for create/update record.

How are request parameters mapped into RenderingModel of magnolia?

Im using Magnolia RenderingModel in combination with Freemarker.
I have URLs like the following:
http://anyPath/context?productTypes=XXXXX&productTypes=YYYYY
my rendering model class looks like:
class MyModel extends RenderingModelImpl {
...
private String[] productTypes;
...
}
However the mentioned array contains only the first value, but not the second.
I checked the behaviour of template directives like ctx.getParameters(). This shows the same behaviour, I get only the first value returned. But if im using ctx.getParameterValues(paramName), it returns both values.
This leads me to following questions:
How would I go, if I want to lookup how the request parameters are mapped into the rendering model, or better:
How can i change the behaviour of that ?
Can anyone acknowledge, that this behaviour is wrong ?
It used to be mentioned in documentation and I believe it still is - if you use .getParameters() you get only first value for multivalue parameter. If you want to get all the values, you need to use .getParameterValues(String param).
From what I understand reasons for that were backward compatibility.
As for changing the behavior, you would need to write your own renderer (e.g. by extending default FreemarkerRenderer and override info.magnolia.rendering.renderer.AbstractRenderer.newModel(Class<T>, Node, RenderableDefinition, RenderingModel<?>) method which instantiates and populates the model class.
Alternatively you can provide fix for above set population method and submit it to Magnolia as a patch. While the .getParameters() behavior is iirc on purpose, the model param population might not be, so you have high chance of getting that changed.

WebApi Odata returning complex type

I have a simple data model consisting of two entities
public class product
{
public int ID {get;set;}
public string Name {get;set;}
}
public class supplier
{
public int ID {get;set;}
public string Name {get;set;}
public IEnumerable<product> products {get;set;}
}
Now from my WebApi odata controller I want to return the supplier with all their products. But I cannot seem to get this working, with it just returning the suppler and effectively stripping the product information. The controller method is a simple Get and GetEntityByKey.
My configuration is as follows.
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<supplier>("supplier");
modelBuilder.EntitySet<product>("product");
Is there a configuration options I'm missing to get this to work?
You didn't mention the URI you're using to get the supplier entity, but I'm going to assume it looks something like: http://.../ServiceRoot.svc/supplier(1). By default in OData, navigation properties are not expanded; that is, by default, requesting a supplier will not include the IDs and Names of the linked products unless you ask for them explicitly via the $expand query option. For example: http://.../ServiceRoot.svc/supplier(1)?$expand=products.
If you don't expand the navigation property, then the products property of the supplier will show up as just a collection of links to the product entities. If you aren't seeing links to the products in the response payload, it could be because you're using the new v3 JSON format of OData, which may omit navigation links that follow the general OData URI conventions (since the client can generate those links itself).
If you include the request URI and payload you're getting back, I can be a bit clearer on what's happening in your situation.
A quick note on terminology: "complex type" in OData usually refers to a structural type that has no identity. A typical example of this could be an Address type, which is a value type that has multiple components (city, country, street, etc.) but doesn't need to have its own key. What you're talking about here are navigations between entities.
Maybe try to use QueryableAttribute. Look here:
Expand support
2) Supporting $select and $expand on single entities through
QueryableAttribute.
Try to use $expand clause
In my situation i had to add a complexType first:
builder.ComplexType();
Additionally I found that different query should be used to get the complex type on UI
Instead of calling http://url/api/AccountDetails?$select=name,accountNumber,balance another url should be used:
http://url/api/AccountDetails?$select=name,accountNumber,balance&$expand=balance
you can only see complex properties like balance via $expand
Also, important to have $expand feature turned on. To do that add it before you add the edm model:
endpoints.Select().Expand().Filter().OrderBy().Count().MaxTop(10);
endpoints.MapODataRoute("odata", "odata", this.GetEdmModel());
See details here: https://stackoverflow.com/a/55476645/2050539

Display properties of child object in Datagridview

How can I display in a datagridview selected properties of an object, and also selected properties of a member object of this first object? I think I will not require binding but rely on hard coding updates, because the updates will initiate on non-UI threads, and I think that will not be so easy to bind. At least I've had problems with that in other project.
Basically I'm looking to understand what different ways I can do this. Maybe with LINQ, or whatever is most suitable. Note: I want to display the data in the same table. As the child/parent is a 1:1 relation.
So example code:
Public Class User
public property Score as Integer
public property Details as UserDetails
End Class
Public Class UserDetails
public property Name as String
public property userName as String
End Class
Hence, I want the table to show columns: Score, Name, UserName
EDIT:
Oh, this was more easy than I thought, seems this will work:
Dim q = (From n in userList Select New With {n.Score, n.Details.Name, n.Details.userName}).ToArray
You can use databinding here, if you use the ITypedList interface to expose the properties you wanted.
ITypedList is very powerful, but somewhat hard to understand, IME. The best tutorial I've found is Tips for binding grids to hierarchical data using the ITypedList interface
For the record, this looks like appropriate solution:
Dim q = (From n in userList Select New With {n.Score, n.Details.Name, n.Details.userName}).ToArray

Resources