Skip to content

Commit

Permalink
Test new Spring Data 3 interfaces and methods introduced in bump
Browse files Browse the repository at this point in the history
(cherry picked from commit 5bd7470)
  • Loading branch information
michalvavrik authored and fedinskiy committed Sep 25, 2024
1 parent f4f84e6 commit 7e793d6
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.quarkus.ts.spring.data.rest;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;

@Entity
public class Magazine {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotBlank(message = "Name may not be blank")
private String name;

@Column(name = "issued_in")
@Min(1800)
private Long issuedIn;

public Magazine() {
}

public Magazine(String name) {
this.name = name;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Magazine withName(String name) {
this.name = name;
return this;
}

public Long getIssuedIn() {
return issuedIn;
}

public void setIssuedIn(Long issuedIn) {
this.issuedIn = issuedIn;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.quarkus.ts.spring.data.rest;

import org.springframework.data.jpa.repository.JpaRepository;

public interface MagazineJpaRepository extends JpaRepository<Magazine, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.ts.spring.data.rest;

import org.springframework.data.repository.ListCrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(path = "magazine-list-crud-rest-repository")
public interface MagazineListCrudRepository extends ListCrudRepository<Magazine, Long> {

Magazine findByName(String name);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.quarkus.ts.spring.data.rest;

import org.springframework.data.repository.ListPagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(path = "magazine-list-paging-sorting-rest-repository")
public interface MagazineListPagingAndSortingRepository extends ListPagingAndSortingRepository<Magazine, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.quarkus.ts.spring.data.rest;

import static jakarta.transaction.Transactional.TxType.REQUIRES_NEW;

import java.util.Objects;

import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import io.quarkus.security.PermissionsAllowed;

@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@PermissionsAllowed("read")
@Path("magazine-resource")
public class MagazineResource {

@Inject
MagazineListCrudRepository magazineRepository;

@Inject
MagazineJpaRepository magazineJpaRepository;

public record MagazineDto(String oldName, String newName) {
}

@Path("{id}")
@GET
public Magazine getMagazine(@PathParam("id") long id) {
return magazineJpaRepository.getReferenceById(id);
}

@Transactional(REQUIRES_NEW) // makes sure we activate transaction here
@PUT
public String updateMagazine(MagazineDto magazineDto) {
Objects.requireNonNull(magazineDto);
Objects.requireNonNull(magazineDto.newName());
Objects.requireNonNull(magazineDto.oldName());

var magazine = magazineRepository.findByName(magazineDto.oldName());
var updatedMagazine = magazine.withName(magazineDto.newName());
magazineRepository.save(updatedMagazine);
return magazineRepository
.findById(magazine.getId())
.map(Magazine::getName)
.orElseThrow(IllegalStateException::new); // we know it does exist by now
}

}
6 changes: 6 additions & 0 deletions spring/spring-data/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ quarkus.security.users.embedded.users.user=user
quarkus.security.users.embedded.roles.admin=admin
quarkus.security.users.embedded.roles.user=user

# remap SecurityIdentity role 'admin' to the 'read' permission
# so that we can test @PermissionsAllowed("read")
quarkus.http.auth.permission.perm-remapping.paths=/magazine-resource*
quarkus.http.auth.permission.perm-remapping.policy=perm-remapping
quarkus.http.auth.policy.perm-remapping.permissions.admin=read

# Named ORM persistence unit
quarkus.hibernate-orm.named.datasource=<default>
quarkus.hibernate-orm.named.packages=io.quarkus.ts.spring.data.nameddatasource
Expand Down
7 changes: 7 additions & 0 deletions spring/spring-data/src/main/resources/import.sql
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@ INSERT INTO article(name, author, library_id) VALUES ('Aeneid','Virgil', 1);
INSERT INTO article(name, author, library_id) VALUES ('Beach House','James Patterson',1);
INSERT INTO article(name, author) VALUES ('Cadillac Desert','Marc Reisner');
INSERT INTO article(name, author) VALUES ('Dagon and Other Macabre Tales','H.P. Lovecraft ');

INSERT INTO magazine(name, issued_in) VALUES ('Vanity Fair', 1990);
INSERT INTO magazine(name, issued_in) VALUES ('The Economist', 1991);
INSERT INTO magazine(name, issued_in) VALUES ('Witch Weekly', 1992);
INSERT INTO magazine(name, issued_in) VALUES ('The New York Time Magazine', 1993);
INSERT INTO magazine(name, issued_in) VALUES ('Time', 1994);
INSERT INTO magazine(name, issued_in) VALUES ('Time', 1995);
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package io.quarkus.ts.spring.data.rest;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;

import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import io.quarkus.test.scenarios.QuarkusScenario;
import io.restassured.common.mapper.TypeRef;
import io.restassured.http.ContentType;

@QuarkusScenario
public class CrudRepositoryRestResourceIT extends AbstractRepositoryRestResourceIT {
Expand Down Expand Up @@ -62,4 +68,57 @@ void createWithEntityConstraintViolation() {
.statusCode(HttpStatus.SC_BAD_REQUEST)
.body(containsString("Name may not be blank"));
}

@Tag("QUARKUS-4958")
@Test
public void testListCrudRepository_findAllMethod() {
var magazines = app.given()
.accept(ContentType.JSON)
.get("magazine-list-crud-rest-repository")
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.as(new TypeRef<List<Magazine>>() {
});
assertEquals(6, magazines.size());
assertTrue(hasMagazineWithTitle(magazines, "Vanity Fair"));
assertTrue(hasMagazineWithTitle(magazines, "The Economist"));
}

@Tag("QUARKUS-4958")
@Test
public void testListCrudRepository_Transactional() {
// tests @Transactional for multiple operations:
// custom method findByName, findById, save
// because Quarkus automatically activates transaction
// for individual methods if we don't declare our annotation,
// but we want to try them all in one transaction

// anonymous user
app.given()
.contentType(ContentType.JSON)
.put("/magazine-resource")
.then()
.statusCode(HttpStatus.SC_UNAUTHORIZED);
// authentication user with insufficient rights
app.given()
.contentType(ContentType.JSON)
.auth().preemptive().basic("user", "user")
.put("/magazine-resource")
.then()
.statusCode(HttpStatus.SC_FORBIDDEN);
// test authorized user
app.given()
.contentType(ContentType.JSON)
.auth().preemptive().basic("admin", "admin")
.body(new MagazineResource.MagazineDto("Witch Weekly", "Harper's Monthly"))
.put("/magazine-resource")
.then()
.statusCode(HttpStatus.SC_OK)
.body(is("Harper's Monthly"));
}

private static boolean hasMagazineWithTitle(List<Magazine> magazines, String title) {
return magazines.stream().anyMatch(m -> title.equals(m.getName()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
import java.util.stream.Collectors;

import org.apache.http.HttpStatus;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import io.quarkus.test.scenarios.QuarkusScenario;
import io.restassured.http.ContentType;

@QuarkusScenario
public class JpaRepositoryRestResourceIT extends AbstractPagingAndSortingRepositoryRestResourceIT {
Expand Down Expand Up @@ -55,4 +58,21 @@ public void testNamedDataSourceJpaRepository() {
assertTrue(expectedItems.containsAll(actualItems));
}

@Tag("QUARKUS-4958")
@Test
public void testJpaRepositoryGetReferencedById() {
app.given()
.accept(ContentType.JSON)
.auth().preemptive().basic("user", "user")
.when().get("magazine-resource/1")
.then()
.statusCode(HttpStatus.SC_FORBIDDEN);
app.given()
.accept(ContentType.JSON)
.auth().preemptive().basic("admin", "admin")
.when().get("magazine-resource/1")
.then()
.statusCode(HttpStatus.SC_OK)
.body("name", Matchers.is("Vanity Fair"));
}
}
Loading

0 comments on commit 7e793d6

Please sign in to comment.