We are using SwaggerSpringMvcPlugin to generate swagger documentation as shown below.
#Bean
public SwaggerSpringMvcPlugin swaggerSpringMvcPlugin(SpringSwaggerConfig springSwaggerConfig) {
log.debug("Starting Swagger");
StopWatch watch = new StopWatch();
watch.start();
SwaggerSpringMvcPlugin swaggerSpringMvcPlugin = new SwaggerSpringMvcPlugin(springSwaggerConfig)
.apiInfo(apiInfo())
.genericModelSubstitutes(ResponseEntity.class)
.includePatterns(DEFAULT_INCLUDE_PATTERN);
swaggerSpringMvcPlugin.build();
watch.stop();
log.debug("Started Swagger in {} ms", watch.getTotalTimeMillis());
return swaggerSpringMvcPlugin;
}
I need to exclude some of the model properties as shown below. I tried using #ApiModelProperty(access="hidden", hidden=true), but they are not excluded. Note that all the propeties of MyModel are displayed including status field. Any help appreciated.
public class MyModel implements Serializable {
....
#ApiModelProperty(access="hidden", hidden=true)
#Column(name = "status")
private String status;
...
}
You can simply use
public class MyModel implements Serializable {
#ApiModelProperty(hidden=true)
private String status;
}
Assuming that you dont want to show this property or allow it to be editable via serialization, I'd say just adding the
#JsonIgnore
Jackson2 annotation on your bean property will tell the model generation to excluded the property from being generated.
Also, keep in mind that, the annotation needs to be placed on the beans on getters or fields depending on how the ObjectMapper serialization/deserialization is configured.
If you merely want to hide the field from the swagger ui and allow modification/serialization of that field, then its not currently possible. However in the next version of the library, we plan to make this possible
Related
I have a project consisting of 2 parts:
ASP.NET API using Entity Framework
.NET MAUI Client App
I use DTOs for comunication from/to the API in order not to expose other properties of my entities. Thanks to this approach I was able to separate Entity data and data that are sent from the API.
At first I used these DTOs also in the MAUI UI. But after some time I started to notice that they contains UI-specific properties, attributes or methods that have no purpose for the API itself, so they are redundant in requests.
EXAMPLE:
1 - API will receive request from MAUI to get exercise based on it's name
2- ExerciseService returns: ExerciseEntity and ExerciseController use AutoMapper to Map ExerciseEntity -> ExerciseDto ommiting ExerciseId field (only admin can see this info in the DB) and returning it in the API response
3 - MAUI receives from the API ExerciseDto. But in the client side it also want to know if data from ExerciseDto are collapsed in the UI. So because of that I add IsCollapsed property into the ExerciseDto. But now this is a redundant property for the API, because I dont want to persist this information in the database.
QUESTIONS:
Should I map these DTOs to new objects on the client side ?
Or how to approach this problem ?
Is there an easier way how to achieve the separation ?
Because having another mapping layer will add extra complexity and a lot of duplicate properties between DTOs and those new client objects.
Normally if you use clean architecture approach your DTOs shoud contain no attributes and other specific data relevant just for some of your projects, to be freely usable by other projects in a form of dependency.
Then you'd have different approaches to consume DTOs in a xamarin/maui application, for example:
APPROACH 1.
Mapping (of course) into a class that is suitable for UI. Here you have some options, use manual mapping, write your own code that uses reflection or use some third party lib using same reflection. Personally using all of them, and when speaking of third party libs Mapster has shown very good to me for api and mobile clients.
APPROACH 2.
Subclass DTO. The basic idea is to deserialize dto into the derived class, then call Init(); if needed. All properties that you manually implemented as new with OnPropertyChanged will update bindings after being popupated by deserializer/mapper and you alse have a backup plan to call RaiseProperties(); for all of the props, even thoses who do not have OnPropertyChanged in place so they can update bindings if any.
Example:
our Api DTO
public class SomeDeviceDTO
{
public int Id { get; set; }
public int Port { get; set; }
public string Name { get; set; }
}
Our derived class for usage in mobile client:
public class SomeDevice : SomeDeviceDTO, IFromDto
{
// we want to be able to change this Name property in run-time and to
// reflect changes so we make it bindable (other props will remain without
// OnPropertyChanged BUT we can always update all bindings in code if needed
// using RaiseProperties();):
private string _name;
public new string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged();
}
}
}
// ADD any properties you need for UI
// ...
#region IFromDto
public void Init()
{
//put any code you'd want to exec after dto's been imported
// for example to fill any new prop with data derived from what you received
}
public void RaiseProperties()
{
var props = this.GetType().GetProperties();
foreach (var property in props)
{
if (property.CanRead)
{
OnPropertyChanged(property.Name);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
public interface IFromDto : INotifyPropertyChanged
{
//
// Summary:
// Can initialize model after it's being loaded from dto
void Init();
//
// Summary:
// Notify all properties were updated
void RaiseProperties();
}
We can get it like: var device = JsonConvert.DeserializeObject<SomeDevice>(jsonOfSomeDeviceDTO);
We then can call Init(); if needed..
Feel free to edit this answer to add more approaches..
Example:
#Value
#Builder(toBuilder = true)
#AllArgsConstructor(access = AccessLevel.PUBLIC)
public class A {
//
// RecordRootable fields
//
#NonNull
private UUID a;
}
Looking at the class file the #NonNull annotation seems to be present if the IntelliJ decompiler is not lying.
Now we want to read the annotation from the Field using Field.getAnnotations or something similar at runtime but nothing is found.
Is there any particular method I can use to read this annotation?
Is there anything extra I need to add to the annotation to be able to read it?
It seems the Annotation has
#Retention(RetentionPolicy.CLASS)
instead of
#Retention(RetentionPolicy.RUNTIME)
so they can't be read reflectively
I have a graph of objects that I'd like to return different views of. I don't want to use Jackson's #JsonViews to implement this. Right now, I use Jackson MixIn classes to configure which fields are shown. However, all my rest methods return a String rather than a type like BusinessCategory or Collection< BusinessCategory >. I can't figure out a way to dynamically configure the Jackson serializer based on what view I'd like of the data. Is there any feature built into Spring to configure which Jackson serializer to use on a per-function basis? I've found posts mentioning storing which fields you want in serialized in thread-local and having a filter send them and another post filtering based on Spring #Role, but nothing addressing choosing a serializer (or MixIn) on a per-function basis. Any ideas?
The key to me thinking a proposed solution is good is if the return type is an object, not String.
Here are the objects in my graph.
public class BusinessCategory implements Comparable<BusinessCategory> {
private String name;
private Set<BusinessCategory> parentCategories = new TreeSet<>();
private Set<BusinessCategory> childCategories = new TreeSet<>();
// getters, setters, compareTo, et cetera
}
I am sending these across the wire from a Spring MVC controller as JSON like so:
#RestController
#RequestMapping("/business")
public class BusinessMVC {
private Jackson2ObjectMapperBuilder mapperBuilder;
private ObjectMapper parentOnlyMapper;
#Autowired
public BusinessMVCfinal(Jackson2ObjectMapperBuilder mapperBuilder) {
this.mapperBuilder = mapperBuilder;
this.parentOnlyMapper = mapperBuilder.build();
parentOnlyMapper.registerModule(new BusinessCategoryParentsOnlyMapperModule());
}
#RequestMapping(value="/business_category/parents/{categoryName}")
#ResponseBody
public String getParentCategories(#PathVariable String categoryName) throws JsonProcessingException {
return parentOnlyMapper.writeValueAsString(
BusinessCategory.businessCategoryForName(categoryName));
}
}
I have configure the serialization in a MixIn which is in turn added to the ObjectMapper using a module.
public interface BusinessCategoryParentsOnlyMixIn {
#JsonProperty("name") String getName();
#JsonProperty("parentCategories") Set<BusinessCategory> getParentCategories();
#JsonIgnore Set<BusinessCategory> getChildCategories();
}
public class BusinessCategoryParentsOnlyMapperModule extends SimpleModule {
public BusinessCategoryParentsOnlyMapperModule() {
super("BusinessCategoryParentsOnlyMapperModule",
new Version(1, 0, 0, "SNAPSHOT", "", ""));
}
public void setupModule(SetupContext context) {
context.setMixInAnnotations(
BusinessCategory.class,
BusinessCategoryParentsOnlyMixIn.class);
}
}
My current solution works, it just doesn't feel very clean.
"categories" : [ {
"name" : "Personal Driver",
"parentCategories" : [ {
"name" : "Transportation",
"parentCategories" : [ ]
} ]
}
Oh yes, I'm using:
spring-boot 1.2.7
spring-framework: 4.1.8
jackson 2.6.3
Others listed here: http://docs.spring.io/spring-boot/docs/1.2.7.RELEASE/reference/html/appendix-dependency-versions.html
In the end, the only process that met my needs was to create a set of view objects which exposed only the fields I wanted to expose. In the grand scheme of things, it only added a small amount of seemingly unnecessary code to the project and made the flow of data easier to understand.
I have a dynamic ActionScript Class that is used to send parameters to a WebService. Some of these parameters are always present, so they are public properties of the Class:
package
{
[Bindable]
public dynamic class WebServiceCriteria
{
public var property1:int;
public var property2:String;
public var property3:String;
public var property4:String;
}
}
But, I am also adding properties at runtime that can change over time:
criteria.runTimeProperty = "1";
I'm not very familiar with using dynamic classes, so I was wondering if it is possible to "remove" the new property. Let's say the next time I call the WebService I don't want that property sent - not even as a null. How can I remove it from the Class instance without creating a new instance each time?
I believe all you'd need to do is this:
delete criteria.runTimeProperty;
or
delete criteria["runTimeProperty"];
Either should do the same thing.
See the delete documentation for specifics.
Suppose I use the [RemoteClass] tag to endow a custom Flex class with serialization intelligence.
What happens when I need to change my object (add a new field, remove a field, rename a field, etc)?
Is there a design pattern for handling this in an elegant way?
Your best bet is to do code generation against your backend classes to generation ActionScript counterparts for them. If you generate a base class with all of your object properties and then create a subclass for it which is never modified, you can still add custom code while regenerating only the parts of your class that change. Example:
java:
public class User {
public Long id;
public String firstName;
public String lastName;
}
as3:
public class UserBase {
public var id : Number;
public var firstName : String;
public var lastName : String;
}
[Bindable] [RemoteClass(...)]
public class User extends UserBase {
public function getFullName() : String {
return firstName + " " + lastName;
}
}
Check out the Granite Data Services project for Java -> AS3 code generation.
http://www.graniteds.org
Adding or removing generally works.
You'll get runtime warnings in your trace about properties either being missing or not found, but any data that is transferred and has a place to go will still get there. You need to keep this in mind while developing as not all your fields might have valid data.
Changing types, doesn't work so well and will often result in run time exceptions.
I like to use explicit data transfer objects and not to persist my actual data model that's used throughout the app. Then your translation from DTO->Model can take version differences into account.