spring-projects / spring-projects/spring-data-rest

Failure to link persistent entities to a target entity's member variable if member variable is of abstract type [DATAREST-214]

Open
#599 0 comments 0 reactions 1 assignee View on GitHub

@odrotbohm is already working on this.

Since Dec 31, 2020.

type: bug
Dominant language
Java
Stars
958
Forks
568
PR merge metrics
No merged PRs in 30d

Description

Aaron Loes opened DATAREST-214 and commented

This is in relation to DATAREST-202.

When attempting to link an entity to the value of another entity, the code is attempting to look up the repository for the linked entity by using the target entities member variable type. This causes issues if the member variable type is an abstract class. Instead the code should look up the repository information of the linked entities via the URL that it is passed and then validate that the type is allowed to be assigned.

Pseudo Example:

Given a Domain Model:

class Farm {
  @DBRef
  List<? extends Animal> animals;
}
abstract class Animal { /*...*/ }
class Horse extends Animal { /*...*/ } // this has a repository
class Cow extends Animal { /*...*/ } // this has a repository
class Pig extends Animal { /*...*/ } // this has a repository

And given a request

PUT /myApp/farm/1234/animals HTTP/1.1
Host: localhost:8080
Content-Length: 96
Content-Type: text/uri-list; charset=UTF-8
Accept: application/json; charset=utf-8
Origin: http://localhost:8080

http://localhost:8080/myApp/horse/6543
http://localhost:8080/myApp/pig/7654
http://localhost:8080/myApp/cow/9457

The code is currently looking for a Repository that handles the "Animal" class. Instead, when going to assign the values to Farm#animals, it should use the URL's given to find the appropriate repository and verify that their types are a subtype of Animal.

I have currently KLUDGED together a fix which meant replacing the RepositoryPropertyReferenceController and modifying its loadPropertyValue method like so:

private Object loadPropertyValue(Class<?> type, String href) {
  String id = href.substring(href.lastIndexOf('/') + 1);
  
  // get url for linked entity
  String repositoryUrl = href.substring(0, href.lastIndexOf('/'));
  
  // get repository name for linked entity
  String repositoryName = repositoryUrl.substring(repositoryUrl.lastIndexOf('/') + 1);
  
  // look up resource metadata for linked entity
  ResourceMetadata metadata = null;
  for (Class<?> domainType : repositories) {
    ResourceMetadata mapping = mappings.getMappingFor(domainType);
    if (mapping.getPath().matches(repositoryName) && mapping.isExported()) {
      metadata = mapping;
      break;
    }
  }
  
  // get type from linked entity if none found, otherwise use default
  if (metadata != null) {
    type = metadata.getDomainType();
  }
  
  return converter.convert(id, STRING_TYPE, TypeDescriptor.valueOf(type));
}

Affects: 2.0 M1 (Codd)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.