findBy URI not working in Spring Data Rest - spring-data-rest

By default, in Spring Data Rest the #Id of the entity is not exposed. In line with the REST rules, we're supposed to use the URI of the resource to refer to it. Given this assumption, the findBy queries should work if you pass a URI to them, but they don't.
For example, say I have a one-to-many relationship between Teacher and Student. I want to find students by teacher.
List<Student> findByTeacher(Teacher teacher)
http://localhost:8080/repositories/students/search/findByTeacher?teacher=http://localhost:8080/repositories/teachers/1
This doesn't work because the framework is attempting to convert the teacher URI to a Long.
I get this error that says "Failed to convert from type java.lang.String to type java.lang.Long".
Am I missing something?

You could expose #Id s by configuring web intializer
//Web intializer
#Configuration
public static class RespositoryConfig extends
RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config) {
config.exposeIdsFor(Teacher.class);
}
}
Its good to change List to Page
List findByTeacher(Teacher teacher)
to
Page<Student> findByTeacher(#Param("teacher) Teacher teacher, Pageable pageable);
Also note #Param annotation is required along with Pageable. The latter is required because return type "Page"
3.Latest snapshots, not milestones work fine

See https://jira.spring.io/browse/DATAREST-502
Depending of your version of Spring Data, it would work as you want or not. If you are with Spring Data 2.4, you need to pass the URI. If you are with a previous version, you need to pass the id.

Related

Field level access Control using Aspects/ Custom Annotation (Spring boot , Microservice)

Currently i am trying to implement authorization on fields , please find the cases from the below
example :
Based on some specific roles which are available in the ThreadLocal , we should be able to determine whether the user is allowed to pass the field in the payload. if the received role is not allowed to pass the attribute to do any Creation or updation we should throw 403
While providing response in the GET API , we should hide few fields which all are annotated with role:"ADMIN" as an example .
For the above example i am trying to use custom annotation Target as Fieldex :
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface CustomScopeFilter {
String[] scopesAllowed() default {"END_USER"};
}
But the custom annotation with FIELD is not working actually , because the implementation class which annotated with #Aspect is not getting called
The above annotation i have used in my DTO on field level
ex :
#Getter
#Setter
class TestDTO {
#CustomScopeFilter(scopesAllowed={"ADMIN"})
private String userRole;
}
Any ideas or suggestion would be very much helpful !! Thanks in advance
tried using Pointcut , joinpoint ,Aspectj, AOP. but those didnot worked on field-level
So i am expecting some suggestions how i can make it work .. or any alternative approach to achieve the same.

Spring Data Rest findByIdIn

We use Spring Data Rest to build simple API for our internal webapp.
Our repositories as below:
public interface BaseRepository<T, ID extends Serializable> {
List<T> findByIdIn(#Param("ids") Collection<ID> ids);
}
public interface FooRepository extends BaseRepository<Foo, String> {}
public interface BarRepository extends BaseRepository<Bar, Long> {}
Our client fetch data, passing ids parameters as below:
GET http://example.com/api/foos/search/findByIdIn?ids=ABC,XYZ --> It works well for String ids.
GET http://example.com/api/bars/search/findByIdIn?ids=1,2,3 --> Got exception for numeric ids.
We got the exception: Parameter value element[1] did not match expected type [java.lang.Long (n/a)]
What's wrong with above repositories? What is the correct way to pass numeric ids?
You may use this library which lets you build advanced search queries: https://github.com/turkraft/spring-filter
You can then simply do:
?filter= ids in (1, 2, 3, 4)
It supports enums, dates, booleans, logical operations, comparisons, and even joins (nested fields).

Id Encryption in spring-hateoas or Spring Rest Data

I have a question about a standard pattern or mechanism in spring-hateoas or Spring Rest Data about encrypting the IDs of the Resources/Entities.
The reason I am asking, a requirement to our project is that we don't deliver the id's of our objects to the outside world and they should not be used in GET Requests as Parameters.
I know, Spring Rest Data and spring-hateoas does not give the ids of the objects unless they are configured so but even that case I can see the ids in links.
I know I can use PropertyEditors or Converters to encrypt/decrypt ids before and after Json serialisation/deseritalisation but I just like to know is there a more standard way?
Thx for answers...
If you have the unique 'business id' property of your resource you can configure SDR to use it instead of the entity ID.
First you have to create lookup method of your entity with this unique property:
public interface MyEntityRepo extends JpaRepository<MyEntity, Long> {
#RestResource(exported = false)
Optional<CatalogResource> findByMyUniqueProperty(String myUniqueProperty);
}
Then use it to configure SDR:
#Component
public class DataRestConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.withCustomEntityLookup()
.forRepository(MyEntityRepo.class, MyEntity::getMyUniqueProperty, MyEntityRepo::findByMyUniqueProperty);
super.configureRepositoryRestConfiguration(config);
}
}
After this customization you will have resource URI like this:
http://localhost:8080/myEntities/myUniquePropertyValue1

Spring Data Rest ignoring #JsonInclude annotation

With or without this annotation, there is a property on my JPA #Entity
#Entity
public class Myentity extends ResourceSupport implements Serializable {
...
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="idrepository")
#JsonInclude(JsonInclude.Include.ALWAYS)
private MyentitySource entitysource;
...
}
that is not being mapped when I return:
#RequestMapping("/myentity/{uuid}")
public ResponseEntity<Myentity> getResourceById(#PathVariable("uuid") UUID uuid) {
Myentity result = myentityRepository.findOne(uuid);
return ResponseEntity.ok(myentityAssembler.toResource(result));
}
myentityAssembler.toResource(result) does contain this MyentitySource entitysource, but the JSON output does not.
The weirdest thing is I have another spring boot hateoas project where I am using the exact same entity, repository, controller, and assembler implementations, with the exact same dependencies and versions on my pom, and a very similar configuration (I am not defining any special jackson mappers or anything, just using the default rest/hateoas configuration), and it does work there: The MyentitySource entitysource property, which is another JPA entity extending ResourceSupport, gets serialized and included into the JSON output.
I have been a couple of hours at it already, but I am quite lost. I have verified this behavior is happening all through the application in both applications: #ManyToOne relations defined on any #Entity are being mapped and present in the JSON output on one application, but not in the other.
How can I get these fields to show up on the JSON output?
entitysource will be included if MyentitySource is not an exported entity. If it is one - what seems to be the case here - then it would be wrong to include it. Including associations could lead to sending the whole database to the client. Moreover it is a separate resource with its own URI. Consequently a link to that URI is included in the response.
CascadeType.ALL implies that Myentity is an aggregate, therefore MyentitySource should not be exported in the first place. That would solve your problem. If my assumption is wrong, then you can still use Projections to get entitysource included. I can refer you to this answer from Spring's Oliver Gierke and the relevant chapter of the documentation.

wcf data services - expose properties of the same type in data model

I'm new to WCF data services. I have a quite simple data model. Some of its properties have the same type, like this:
public IQueryable<IntegerSum> HouseholdGoodsSums
{
get
{
return GetData<IntegerSum>(DefaultProgramID, "rHouseholdGoodsPrice", IntegerSumConverter);
}
}
public IQueryable<IntegerSum> StructureSums
{
get
{
return GetData<IntegerSum>(DefaultProgramID, "rStructurePrice", IntegerSumConverter);
}
}
The IntegerSum is a very very simple class:
[DataServiceKey("Amount")]
public class IntegerSum
{
public int Amount { get; set; }
}
When I navigate to my service in a web browser, I see the following error message:
The server encountered an error processing the request. The exception message is 'Property 'HouseholdGoodsSums' and 'StructureSums' are IQueryable of types 'IntegrationServices.PropertyIntegrationServices.IntegerSum' and 'IntegrationServices.PropertyIntegrationServices.IntegerSum' and type 'IntegrationServices.PropertyIntegrationServices.IntegerSum' is an ancestor for type 'IntegrationServices.PropertyIntegrationServices.IntegerSum'. Please make sure that there is only one IQueryable property for each type hierarchy.'.
When I get rid of one of these two properties, the service starts working.
I searched for this error message in google, but haven't found a solution.
Is it really not allowed to have two properties with the same type in a data model? If so, why?
Comrade,
To address the error first, you're running into a limitation in the Reflection provider. Specifically, the Reflection provider doesn't support MEST.
That said, there are better approaches to achieve what you're trying to achieve. You should probably not make IntegerSum an entity type (an entity type is a uniquely identifiable entity, which doesn't really fit your scenario). While you can't expose that directly, you can expose it as a service operation. That seems much closer to what you're trying to achieve.
A couple of ways to distinguish between whether or not something should be an entity:
If it has a key already, such as a PK in a database, it should probably be an entity type
If you need to create/update/delete the object independently, it must be an entity type
HTH,
Mark