Challenge

Six endpoints, and what each publishes

25 minintermediate115 yrs

Edge cases. You have to reason, and two valid fixes differ.

What this teaches

  • The same defect produces disclosure, coupling, recursion or an N+1 depending on the endpoint
  • A DTO built after the transaction ends fixes nothing
  • Accepting an entity is a write vulnerability, not a design preference
  • Annotations that stop the recursion make the entity worse for the next consumer

Starter

Starter.java
import java.util.*;

/**
 * Challenge: six endpoints from one service. All six return or accept an
 * entity somewhere. They fail in four different ways.
 */
public class Starter {

    // ── 1 ─────────────────────────────────────────────────────────────────
    //   @GetMapping("/users/{id}")
    //   public User get(@PathVariable Long id) { return repo.findById(id).orElseThrow(); }
    //   User has: id, email, displayName, passwordHash, lastLoginIp, deleted

    // ── 2 ─────────────────────────────────────────────────────────────────
    //   @PostMapping("/users")
    //   public User create(@RequestBody User user) { return repo.save(user); }

    // ── 3 ─────────────────────────────────────────────────────────────────
    //   @GetMapping("/authors/{id}")
    //   public Author get(...)          // Author has List<Book>, Book has Author

    // ── 4 ─────────────────────────────────────────────────────────────────
    //   @GetMapping("/orders/{id}")
    //   public OrderResponse get(@PathVariable Long id) {
    //       return mapper.toResponse(orderService.find(id));   // service is @Transactional
    //   }

    // ── 5 ─────────────────────────────────────────────────────────────────
    //   @GetMapping("/books/{id}")
    //   public Book get(...)            // Book.author is annotated @JsonIgnore

    // ── 6 ─────────────────────────────────────────────────────────────────
    //   @GetMapping("/orders")
    //   public List<Order> list()       // Order.lines is LAZY, open-in-view is default

    public static void main(String[] args) {

        // TODO 1: classify each endpoint by which problem it has — disclosure,
        // schema coupling, recursion, or queries during serialisation. Some
        // have more than one.

        // TODO 2: for endpoint 1, list every field a client can see today.
        // Then say what happens when someone adds a `mfa_secret` column.

        // TODO 3: for endpoint 2, list every field a caller can SET. Which one
        // turns a create into an update, and against which row?

        // TODO 4: endpoint 4 has a DTO and still fails. Explain where the
        // mapping happens and why that undoes the DTO. Move it.

        // TODO 5: endpoint 5 is "fixed". Say what a consumer who needs the
        // author from a book must now do, and who ends up paying for that fix.

        // TODO 6: endpoint 6 works in production and is slow. Count the
        // queries for a page of 50 orders, and say in which phase they happen.
        // Then say what changes if open-in-view is turned off — the answer is
        // not "it gets faster".

        // TODO 7: one change fixes five of these six. Name it, name the one it
        // does not fix, and say what that one needs instead.
    }
}

Run it locally:

cd exercises/java/jpa-hibernate/entity-at-the-boundary/02-challenge
javac Starter.java -d /tmp/out && java -cp /tmp/out Starter

Hints

  1. Hint 1

    For each endpoint, answer three things: what fields does it publish, what can a caller set, and where does the mapping happen relative to the transaction.

  2. Hint 2

    One endpoint has a DTO and still throws. The DTO is not the problem.

  3. Hint 3

    One endpoint is only dangerous in the request direction. List what a caller could set that the developer never considered.

  4. Hint 4

    One is fixed with @JsonIgnore and is worse for it. Say who pays.

Done when

  • Each endpoint classified by which of the four problems it has
  • The DTO-that-still-throws is diagnosed as a placement problem
  • The request-body endpoint has a list of fields a caller could set, including the dangerous one
  • You said what the @JsonIgnore endpoint costs the next consumer
  • One change is named that fixes five of the six, and its cost is stated

← Back to Should you return a JPA entity from a REST controller?