Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data Rest - Custom Json Schema / Alps?

My need is to give information about data constraints or default values to the client app that will use the API. The schema or the ALPS generated by Spring Data Rest seems to be a good place to put this information.

But the part about documenting the API is a bit quick in the official reference documentation, and I can't find fully documented example in the community. I've tried to read the code of PersistentEntityToJsonSchemaConverter to have a insight of the offered possibilities, but the headache arrived first.

I know there is the @Description annotation that I can put on Entities and Properties that will change the title field of the schema. I know the same fields can be modified in rest-messages.properties

Is there other fields that can be modified by annotations or configuration files ? Putting default or constraints information in this description field really feels like not using it straight.

like image 511
JR Utily Avatar asked Oct 28 '15 17:10

JR Utily


1 Answers

The question is almost old, I don't know if you have already found a solution.

Anywhere, you can build a completely custom ALPS profiling information if you build two custom converters that replace the converters used by Spring.

The first one needs to extends the converter org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter.

Here a possible implementation:

public class CustomAlpsJsonHttpMessageConverter extends AlpsJsonHttpMessageConverter {

    public CustomAlpsJsonHttpMessageConverter(RootResourceInformationToAlpsDescriptorConverter converter) {
        super(converter);
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return super.canWrite(clazz, mediaType);
    }

    @Override
    public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
        return super.canRead(type, contextClass, mediaType);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
            ServerHttpResponse response) {
        return super.beforeBodyWrite(body, returnType, selectedContentType, selectedConverterType, request, response);
    }

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return converterType.equals(AlpsJsonHttpMessageConverter.class) 
                || converterType.equals(CustomAlpsJsonHttpMessageConverter.class);
    }

}

The second one needs to extends the converter org.springframework.data.rest.webmvc.alps.RootResourceInformationToAlpsDescriptorConverter.

The RootResourceInformationToAlpsDescriptorConverter only have two public resources: the constructor and the "convert" method.

You may to overwrite every single private field/method of that class if you want to have a custom behaviour.

Pay attention that the "supports" method of your CustomAlpsJsonHttpMessageConverter will need to matches the given "converterType" with your new CustomAlpsJsonHttpMessageConverter class.

At that point you can customize the "convert" method of the class RootResourceInformationToAlpsDescriptorConverter, simply ovverriding it in your CustomRootResourceInformationToAlpsDescriptorConverter.

Finally, you have to register the two converters in the Application Context. In order to do that, you can extend the RepositoryRestMvcConfiguration class, and in your CustomRepositoryRestMvcConfiguration you will need to @Override the methods "alpsJsonHttpMessageConverter()" and "alpsConverter()".

Add also the @Bean annotation in the two ovverriding custom methods, like this:

@Bean
@Override
public AlpsJsonHttpMessageConverter alpsJsonHttpMessageConverter() {
    return new CustomAlpsJsonHttpMessageConverter(alpsConverter());
}

@Bean
@Override
public RootResourceInformationToAlpsDescriptorConverter alpsConverter() {
    Repositories repositories = repositories();
    PersistentEntities persistentEntities = persistentEntities();
    RepositoryEntityLinks entityLinks = entityLinks();
    MessageSourceAccessor messageSourceAccessor = resourceDescriptionMessageSourceAccessor();
    RepositoryRestConfiguration config = config();
    ResourceMappings resourceMappings = resourceMappings();

    return new CustomRootResourceInformationToAlpsDescriptorConverter(associationLinks(), repositories, persistentEntities,
            entityLinks, messageSourceAccessor, config, objectMapper(), enumTranslator());
}

So you can have a completely custom ALPS, if you need.

I have tried this solution to build custom profiling links, and it works perfectly.

like image 127
Alessandro C Avatar answered Nov 05 '22 04:11

Alessandro C