loesak
loesak

Reputation: 1623

Spring Data Rest ResourceProcessor not applied on Projections

I am using a ResourceProcessor to add additional links to my resource object when listed in a collection or fetched individually. However, when I apply a projection (or an excerpt project) to my repository, the ResourceProcessor does not get run and thus my links for that resource do not get created. Is there a means to allow my custom resource links to be added to a resource regardless of how the resource content is projected?

Upvotes: 3

Views: 1122

Answers (2)

Denis
Denis

Reputation: 1271

Here is a generic solution:

@Component
public class ProjectionProcessor implements RepresentationModelProcessor<EntityModel<TargetAware>> {

    private final RepresentationModelProcessorInvoker processorInvoker;

    public ProjectionProcessor(@Lazy RepresentationModelProcessorInvoker processorInvoker) {
        this.processorInvoker = processorInvoker;
    }

    @Override
    public EntityModel<TargetAware> process(EntityModel<TargetAware> entityModel) {
        TargetAware content = entityModel.getContent();
        if (content != null) {
            entityModel.add(processorInvoker.invokeProcessorsFor(EntityModel.of(content.getTarget())).getLinks());
        }
        return entityModel;
    }

}

It gets links for original entities and adds them to corrseponding projections.

Upvotes: 0

Mathias Dpunkt
Mathias Dpunkt

Reputation: 12184

I think this issue is describing your case: https://jira.spring.io/browse/DATAREST-713

Currently, spring-data-rest does not offer functionality to solve your problem.

We are using a little workaround that still needs a separate ResourceProcessor for each projection but we do not need to duplicate the link logic:

We have a base class that is able to get the underlying Entity for a Projection and invokes the Entity's ResourceProcessor and applies the links to the Projection. Entity is a common interface for all our JPA entities - but I think you could also use org.springframework.data.domain.Persistable or org.springframework.hateoas.Identifiable.

/**
 * Projections need their own resource processors in spring-data-rest.
 * To avoid code duplication the ProjectionResourceProcessor delegates the link creation to
 * the resource processor of the underlying entity.
 * @param <E> entity type the projection is associated with
 * @param <T> the resource type that this ResourceProcessor is for
 */
public class ProjectionResourceProcessor<E extends Entity, T> implements ResourceProcessor<Resource<T>> {

    private final ResourceProcessor<Resource<E>> entityResourceProcessor;

    public ProjectionResourceProcessor(ResourceProcessor<Resource<E>> entityResourceProcessor) {
        this.entityResourceProcessor = entityResourceProcessor;

    }

    @SuppressWarnings("unchecked")
    @Override
    public Resource<T> process(Resource<T> resource) {
        if (resource.getContent() instanceof TargetAware) {
            TargetAware targetAware = (TargetAware) resource.getContent();
            if (targetAware != null
                    && targetAware.getTarget() != null
                    && targetAware.getTarget() instanceof Entity) {
                E target = (E) targetAware.getTarget();
                resource.add(entityResourceProcessor.process(new Resource<>(target)).getLinks());
            }
        }
        return resource;
    }

}   

An implementation of such a resource processor would look like this:

@Component
public class MyProjectionResourceProcessor extends ProjectionResourceProcessor<MyEntity, MyProjection> {

    @Autowired
    public MyProjectionResourceProcessor(EntityResourceProcessor resourceProcessor) {
        super(resourceProcessor);
    }
}

The implementation itself just passes the ResourceProcessor that can handle the entity class and passes it to our ProjectionResourceProcessor. It does not contain any link creation logic.

Upvotes: 2

Related Questions