fabiodocarmo / boot-data-rest-events-sample

Example of application that uses Spring Data Events to perform validation.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Spring Data REST with Event Example

Utilize events in Spring Data REST to perform pre and post operations with @RepositoryEventHandler.

Background

Spring Data REST helps developer to create REST application with little implementation as it turns Repository classes into REST endpoint. In this example we will explore how we can perform actions before and after persisting an entity using @RepositoryEventHandler and its related annotations.

Entity and Repository Classes

There are two @Entity classes; Author and Book, and JpaRepository classes; AuthorRepository and BookRepository.

Author

Author @Entity consists of id, name, and status:

@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Author {

    @Id
    @GeneratedValue
    private Long id;

    @NotBlank
    private String name;

    @OneToMany(mappedBy = "author", cascade = CascadeType.REMOVE)
    private Set<Book> books;

    private Status status;

    public enum Status {

        ACTIVE,

        INACTIVE

    }
}

While its JpaRepository class contains no additional methods:

public interface AuthorRepository extends JpaRepository<Author, Long> {
}

Book

Book @Entity consists of id, author, and title fields:

@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    @JoinColumn
    @ManyToOne(optional = false)
    @NotNull
    private Author author;

    @NotBlank
    private String title;

}

Same as Author, its repository contains no additional methods:

public interface BookRepository extends JpaRepository<Book, Long> {
}

As we can see, both classes are normal JPA entity classes without any additional configurations. Once we boot the application there will be two REST endpoints available; /authors and /books.

Add Author Validation

Next is to add a validation against Book.author where we will ensure that an Author of a Book must be ACTIVE before adding it into the database. For this we will use @RepositoryEventHandler with @HandleBeforeCreate.

By annotating our class with RepositoryEventHandler, we are informing Spring that this class will manage repository related events. Along with it we will use HandleBeforeCreate, which indicate that the method need to be executed before persisting given @Entity.

We will implement our validation in AuthorRepositoryEventHandler:

@RepositoryEventHandler
@AllArgsConstructor
@SuppressWarnings("unused")
public class AuthorRepositoryEventHandler {

    @HandleBeforeCreate
    public void validateAuthorStatus(Book book) {
        Author author = book.getAuthor();

        Assert.isTrue(Author.Status.ACTIVE == author.getStatus(), "book author must be active");
    }

}

As we can see, the method validateAuthorStatus takes Book as a parameter and validate status of an Author to make sure that the author is still ACTIVE.

Configuration Class

Next is to inform the application that we would like to register AuthorRepositoryEventHandler as a Bean and thus will be triggered when related entity is being used. This can be found in RepositoryHandlerConfiguration:

@Configuration
public class RepositoryHandlerConfiguration {

    @Bean
    public AuthorRepositoryEventHandler authorRepositoryEventHandler() {
        return new AuthorRepositoryEventHandler();
    }

}

Verify Implementation

As always, we will verify our implementation through an integration tests. In the following test we will create an Author with status INACTIVE and followed by creating a Book that ties to the Author.

We will expect that it will return Internal Server Error with a message book author must be active:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class BookRepositoryRestTests {

    @Autowired
    private MockMvc mvc;

    @Test
    public void createBookWithInactiveAuthor() throws Exception {
        String authorUri = getAuthorUri(INACTIVE);

        JSONObject request = new JSONObject();

        request.put("author", authorUri);
        request.put("title", "If");

        mvc.perform(
                post("/books")
                        .content(request.toString())
        )
                .andExpect(status().isInternalServerError())
                .andExpect(jsonPath("$.message", is("book author must be active")));
    }

    private String getAuthorUri(Author.Status status) throws Exception {
        JSONObject request = new JSONObject();

        request.put("name", "Rudyard Kipling");
        request.put("status", status.name());

        return mvc.perform(
                post("/authors")
                        .content(request.toString())
        )
                .andExpect(status().isCreated())
                .andReturn().getResponse().getHeader(LOCATION);
    }

}

With a correct implementation, the test above should pass.

About

Example of application that uses Spring Data Events to perform validation.

License:The Unlicense


Languages

Language:Java 100.0%