Zbyt wiele typów w jednej metodzie.

0

Witam, udało mi się zrobić coś w IntelliJ IDEA czego nie do końca rozumiem. Podoba mi się wsparcie ze strony IDE, pomaga budować poczwarki :) No ale czym dalej w las tym więcej drzew, bo konserwacja złego kodu jest trudna. Proszę o pomoc, wyjaśnienie czemu IDE podpowiada tak dziwne typy zagnieżdżone i jak zrobić by przybrało to klarowną postać. Piszę REST-API.

@GetMapping("/{id}")
    public ResponseEntity<Optional<EntityModel<Owner>>> getOwner(@PathVariable("id") Integer id) {
        var filteredOwners = findOwnerById(id);
        if (findOwnerById(id).isEmpty()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        } else {
            return ResponseEntity.ok()
                    .body(filteredOwners.map(HateoasConverter::mapToEnityModel));
        }
    }

public class HateoasConverter {
    public static EntityModel<Owner> mapToEnityModel(Owner owner) {
        EntityModel<Owner> ownerModel = new EntityModel<>(owner);
        ownerModel.add(linkTo(
                methodOn(OwnerController.class).getOwner(owner.getId()))
                .withSelfRel());
        return ownerModel;
    }
}

private Optional<Owner> findOwnerById(Integer id) {
        return ownersList.stream()
                .filter(owner -> owner.getId().equals(id))
                .findAny();
 }

Owner to po prostu bean, chodzi o zwracanie kodu odpowiedzi a także self-referencji. Metodę mapToEnityModel daje się wstawić do findOwnerById ale wtedy nie wchodzi warunek findOwnerById(id).isEmpty() Zdaje mi się, że za mało wiem o typie Optional i coś pomieszałem :)

3

Nie tak się używa klasy Optional. Powinno być map().orElse() lub coś podobnego

3

Nie musisz tak nawet kombinować. Spring jest sprytny i ogarnia w Optionale. Jak zrobisz response entity z optionala to magicznie zrobi z niego 404 jak będzie pusty. Teraz nie działa bo robisz jakieś dziwne ResponseEntity<Optional<...

    @GetMapping("/{id}")
    public ResponseEntity<EntityModel<Owner>> getOwner(@PathVariable("id") Integer id) {
        return ResponseEntity.of(findOwnerById(id).map(HateoasConverter::mapToEnityModel));
    }

efekt taki sam i tym razem będzie działać ;]

0

Wracam do wątku bo REST-API się rozwija, i mam pytanko jak uprościć:

@GetMapping
    public ResponseEntity<CollectionModel<EntityModel<Owner>>> getAllOwners() {
        CollectionModel<EntityModel<Owner>> collectionModel = new CollectionModel<>(
                ownersList.stream().map(HateoasBuilder::mapToEnityModel)
                        .collect(Collectors.toList()));
        collectionModel.add(linkTo(OwnerController.class).withSelfRel());
        collectionModel.add(linkTo(methodOn(OwnerController.class)
                        .getOwner(null)).withRel("ownerById"));
        message.getInfo("V3 :: getAllOwners()", collectionModel);
        return ResponseEntity.ok().header("Cache-Control", "max-age" + "=300")
                .body(collectionModel);
    }

Poprzednio bardzo mi pomogliście :)

3

Po pierwsze NIE PAKOWAĆ LOGIKI DO KONTROLERA. Przenieś tworzenie tego CollectionModel<EntityModel<Owner>> do jakiegoś serwisu a nie rób tego tutaj. W kontrolerze możesz sobie najwyżej robić rzeczy zwiazane z http -> headery, status code itd.
A dalej to nie bardzo rozumiem co ci mamy uprościć skoro taki masz kod. Taką masz logikę beznesową to co ci na to poradzimy? Ja bym osobiście zrobił tu jakiegoś ludzkiego buildera żeby się dało czytać ten kod przynajmniej. Jakieś

CollectionModel<EntityModel<Owner>> collectionModel = CollectionModel.builder()
  .withX()
  .withY()
  .withZ()
  .build();

A te dziwne rzeczy ukrył gdzieś w środku.

2

Poczytaj o SRP - ten kod robi wiele rzeczy:

  1. Pobiera listę obiektów do zwrócenia
  2. Pakuje wszystko w HATEOAS-a
  3. Ustawia cache-control

Spróbuj doprowadzić swoją metodę do takiej postaci:

@GetMapping
ResponseEntity<CollectionModel<EntityModel<Owner>>> getAllOwners() {
    var ownersList = ownersRepository.findAll(); // na marginesie - to jest złe, bo nie ma stronicowania
    var hateosed = buildHateos(ownersList);
    return cacheable(ResponseEntity.ok(), 300) // 300 to do stałej CACHE_TTL_MILIS
                .body(hateosed);
}

A tak w ogóle... po co Ci ten hejtoas? ;)

0

HATEOAS-a level wyżej w byciu Restful - tak :)
To jak strzelanie do armaty do królika, ale cel tej aplikacji to właśnie pokazanie modelu Richardson'a. Nie sądziłem, że tak mocno urośnie mi ten kontroler, że kompozycja tego kodu stanie się problemem. Trudno mi to przerobić, a coraz więcej warstw abstrakcji. Dużo jest napisane jak REST-API robić a niewiele jak robić by miało klarowną formę.

@GetMapping(params = {"owner_id", "kind"})
    public ResponseEntity<CollectionModel<EntityModel<Payments>>> filterPaymentsByKindAndOwnerId(@RequestParam("owner_id") Integer owner_id, @RequestParam("kind") String kind) {
        var checkPayments = findPaymentsByKindAndOwnerId(owner_id, kind);
        var collectionModel = new CollectionModel<>(
                paymentsList.stream().filter(
                        payments -> kind.equals(payments.getKind()) && (owner_id.equals(payments.getOwnerId()))
                ).map(HateoasBuilder::mapToEnityModel).collect(Collectors.toList())
        );
        linkToPaymentSelf(collectionModel, "allPayments");
        queryPaymentKindAndOwner(collectionModel, "self", owner_id, kind);
        if (checkPayments.isEmpty()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        } else {
            return ResponseEntity.ok().body(collectionModel);
        }
    }

Do tej formy doszedłem by zrobić obsługę tych linków. Wygląda koszmarnie ale może mi się wydaje :)

2

A ty dalej z tym

        if (checkPayments.isEmpty()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        } else {
            return ResponseEntity.ok().body(collectionModel);
        }

Mimo że można jakoś sprytniej ;]

1 użytkowników online, w tym zalogowanych: 0, gości: 1