Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limitations of nested embedded resources through private HalRepresentationModel #1870

Open
R-o-la-n-d opened this issue Nov 4, 2022 · 0 comments

Comments

@R-o-la-n-d
Copy link

R-o-la-n-d commented Nov 4, 2022

We have a REST service which is implement with SpringBoot and HATEOAS. The following method exists within the WebService

@Override
public ResponseEntity<?> getCar(String carId) {
        Car car = carService.findById(carId).orElseThrow(() -> {
                throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Car not found!");
        });
               
       // toDTO & HATEOAS Links
       CarItem carItem = carModelAssembler.toModel(car);
         
       HalModelBuilder builder = HalModelBuilder.halModelOf(carItem);
       builder.embed(garageModelAssembler.toModel(car.getGarage()), LinkRelation.of("garage"));
            
       return ResponseEntity.ok(builder.build());
}
 

The response looks like this (abbreviated):

{
   "id":"xyu",
   "garageId":"096a07ea-3970-4968-9cd9-4756ae6a9cb1",
   "_embedded": {
       "garage":{
         "id":"abc",
         "location":"XY",
         "_links":{
            "self":{
               "href":"https://localhost:8443/api/v1/garages/096a07ea-3970-4968-9cd9-4756ae6a9cb1"
            },
        }
     }
  },
  "_links": {
    "self": {
      "href":"https://localhost:8443/api/v1/cars/d589852f-0b3f-4025-86cb-2fa3b35a1339"
    }
  }
}

We would also like to output the embedded objects in our paged responses. We came up with the following as a solution:

@Component
public class CarModelAssembler implements RepresentationModelAssembler<Car, RepresentationModel<CarItem>> {
        private GarageModelAssembler garageModelAssembler;
        private CarMapper carMapper;
 
        public BuildingModelAssembler(CarMapper carMapper, GarageModelAssembler garageModelAssembler) {
                this.carMapper = carMapper;
                this.garageModelAssembler = garageModelAssembler;
        }
 
        @Override
        public RepresentationModel<CarItem> toModel(Car car) {
                return toModel(car, true);
        }
       
        public RepresentationModel<CarItem> toModel(Car car, boolean embedded) {
                CarItem carItem = locationMapper.CarToCarItem(car);
                HalModelBuilder builder = HalModelBuilder.halModelOf(carItem);
               
                // add Links
                builder.link(linkTo(methodOn(CarController.class).getBuilding(carItem.getId())).withSelfRel());
                builder.link(linkTo(methodOn(GarageController.class).getGarage(carItem.getGarage.getId())).withRel("garage"));
               
                if(embedded) {
                        builder.embed(garageModelAssembler.toModel(car.getGarage(), false));
                }
               
                return builder.<CarItem>build();
        }
}

The call in the controller is then as follows:
CollectionModel carItems = pagedResourcesAssembler.toModel(pCars, carModelAssembler);

The whole thing works in principle, but I don't like the fact that:
a. - I have to pack another RepresentationModel (RepresentationModel) around my CarItem (extends RepresentationModel).
b. - I still have to install the embedded switch in the assembler so that there are no infinite nestings
c. - I have to inject the required assemblers for the embedded objects everywhere, which means that sooner or later I have to work with @lazy

Unfortunately, I cannot use the HalRepresentationModel directly because it is private

I'm hoping someone here has a better idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant