for customer findAll()->findCustomerByOptionalPrefix(...)

This commit is contained in:
Michael Hoennig 2022-08-04 09:09:06 +02:00
parent 8fb92f9978
commit 57cf316c00
6 changed files with 104 additions and 37 deletions

View File

@ -22,13 +22,14 @@ public class CustomerController {
@Transactional @Transactional
public List<CustomerEntity> listCustomers( public List<CustomerEntity> listCustomers(
@RequestHeader(value = "current-user") String userName, @RequestHeader(value = "current-user") String userName,
@RequestHeader(value = "assumed-roles", required = false) String assumedRoles @RequestHeader(value = "assumed-roles", required = false) String assumedRoles,
@RequestParam(required = false) String prefix
) { ) {
context.setCurrentUser(userName); context.setCurrentUser(userName);
if (assumedRoles != null && !assumedRoles.isBlank()) { if (assumedRoles != null && !assumedRoles.isBlank()) {
context.assumeRoles(assumedRoles); context.assumeRoles(assumedRoles);
} }
return customerRepository.findAll(); return customerRepository.findCustomerByOptionalPrefix(prefix);
} }
@PostMapping(value = "/api/customers") @PostMapping(value = "/api/customers")
@ -48,5 +49,4 @@ public class CustomerController {
} }
return customerRepository.save(customer); return customerRepository.save(customer);
} }
} }

View File

@ -1,11 +1,21 @@
package net.hostsharing.hsadminng.hs.hscustomer; package net.hostsharing.hsadminng.hs.hscustomer;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
public interface CustomerRepository extends JpaRepository<CustomerEntity, UUID> { public interface CustomerRepository extends Repository<CustomerEntity, UUID> {
Optional<CustomerEntity> findByUuid(UUID id);
@Query("SELECT c FROM CustomerEntity c WHERE :prefix is null or c.prefix like concat(:prefix, '%')")
List<CustomerEntity> findCustomerByOptionalPrefix(@Param("prefix") String prefix);
CustomerEntity save(final CustomerEntity entity);
List<CustomerEntity> findByPrefixLike(final String prefix);
} }

View File

@ -111,11 +111,18 @@ declare
objectUuid uuid; objectUuid uuid;
begin begin
if TG_OP = 'INSERT' then if TG_OP = 'INSERT' then
insert if NEW.uuid is null then
into RbacObject (objectTable) insert
values (TG_TABLE_NAME) into RbacObject (objectTable)
returning uuid into objectUuid; values (TG_TABLE_NAME)
NEW.uuid = objectUuid; returning uuid into objectUuid;
NEW.uuid = objectUuid;
else
insert
into RbacObject (uuid, objectTable)
values (NEW.uuid, TG_TABLE_NAME)
returning uuid into objectUuid;
end if;
return NEW; return NEW;
else else
raise exception 'invalid usage of TRIGGER AFTER INSERT'; raise exception 'invalid usage of TRIGGER AFTER INSERT';
@ -138,7 +145,8 @@ create table RbacRole
( (
uuid uuid primary key references RbacReference (uuid) on delete cascade, uuid uuid primary key references RbacReference (uuid) on delete cascade,
objectUuid uuid references RbacObject (uuid) not null, objectUuid uuid references RbacObject (uuid) not null,
roleType RbacRoleType not null roleType RbacRoleType not null,
unique (objectUuid, roleType)
); );
create type RbacRoleDescriptor as create type RbacRoleDescriptor as
@ -753,7 +761,7 @@ grant all privileges on all tables in schema public to restricted;
*/ */
drop view if exists rbacrole_rv; drop view if exists rbacrole_rv;
create or replace view rbacrole_rv as create or replace view rbacrole_rv as
select r.*, o.objectTable, select DISTINCT r.*, o.objectTable,
findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName
from rbacrole as r from rbacrole as r
join rbacobject as o on o.uuid=r.objectuuid join rbacobject as o on o.uuid=r.objectuuid

View File

@ -12,7 +12,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -27,13 +27,13 @@ class CustomerControllerRestTest {
CustomerRepository customerRepositoryMock; CustomerRepository customerRepositoryMock;
@Test @Test
void apiCustomersWillReturnCustomersFromRepository() throws Exception { void apiCustomersWillReturnAllCustomersFromRepositoryIfNoCriteriaGiven() throws Exception {
// given // given
when(customerRepositoryMock.findAll()).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy));
// when // when
final var pacs = mockMvc.perform(MockMvcRequestBuilders mockMvc.perform(MockMvcRequestBuilders
.get("/api/customers") .get("/api/customers")
.header("current-user", "mike@hostsharing.net") .header("current-user", "mike@hostsharing.net")
.accept(MediaType.APPLICATION_JSON)) .accept(MediaType.APPLICATION_JSON))
@ -44,4 +44,23 @@ class CustomerControllerRestTest {
.andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix()))) .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix())))
.andExpect(jsonPath("$[1].reference", is(TestCustomer.yyy.getReference()))); .andExpect(jsonPath("$[1].reference", is(TestCustomer.yyy.getReference())));
} }
@Test
void apiCustomersWillReturnMatchingCustomersFromRepositoryIfCriteriaGiven() throws Exception {
// given
when(customerRepositoryMock.findCustomerByOptionalPrefix("x")).thenReturn(asList(TestCustomer.xxx));
// when
mockMvc.perform(MockMvcRequestBuilders
.get("/api/customers")
.header("current-user", "mike@hostsharing.net")
.param("prefix", "x")
.accept(MediaType.APPLICATION_JSON))
// then
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix())));
}
} }

View File

@ -27,10 +27,10 @@ class CustomerControllerUnitTest {
void apiCustomersWillReturnCustomersFromRepository() throws Exception { void apiCustomersWillReturnCustomersFromRepository() throws Exception {
// given // given
when(customerRepositoryMock.findAll()).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy));
// when // when
final var pacs = customerController.listCustomers("mike@hostsharing.net", null); final var pacs = customerController.listCustomers("mike@hostsharing.net", null, null);
// then // then
assertThat(pacs).hasSize(2); assertThat(pacs).hasSize(2);
@ -42,10 +42,10 @@ class CustomerControllerUnitTest {
void findAllWithAssumedCustomerAdminRole() throws Exception { void findAllWithAssumedCustomerAdminRole() throws Exception {
// given // given
when(customerRepositoryMock.findAll()).thenReturn(singletonList(TestCustomer.yyy)); when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(singletonList(TestCustomer.yyy));
// when // when
final var pacs = customerController.listCustomers("mike@hostsharing.net", "customer#yyy.admin"); final var pacs = customerController.listCustomers("mike@hostsharing.net", "customer#yyy.admin", null);
// then // then
assertThat(pacs).hasSize(1); assertThat(pacs).hasSize(1);

View File

@ -1,8 +1,6 @@
package net.hostsharing.hsadminng.hs.hscustomer; package net.hostsharing.hsadminng.hs.hscustomer;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.hscustomer.CustomerEntity;
import net.hostsharing.hsadminng.hs.hscustomer.CustomerRepository;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -40,13 +38,17 @@ class CustomerRepositoryIntegrationTest {
currentUser("mike@hostsharing.net"); currentUser("mike@hostsharing.net");
// when // when
final var newCustomer = new CustomerEntity(
UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com"); final var attempt = attempt(em, () -> {
final var result = customerRepository.save(newCustomer); final var newCustomer = new CustomerEntity(
UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com");
return customerRepository.save(newCustomer);
});
// then // then
assertThat(result).isNotNull().extracting(CustomerEntity::getUuid).isNotNull(); assertThat(attempt.wasSuccessful()).isTrue();
assertThatCustomerIsPersisted(result); assertThat(attempt.returnedResult()).isNotNull().extracting(CustomerEntity::getUuid).isNotNull();
assertThatCustomerIsPersisted(attempt.returnedResult());
} }
@Test @Test
@ -88,8 +90,8 @@ class CustomerRepositoryIntegrationTest {
} }
private void assertThatCustomerIsPersisted(final CustomerEntity saved) { private void assertThatCustomerIsPersisted(final CustomerEntity saved) {
final var found = customerRepository.findById(saved.getUuid()); final var found = customerRepository.findByUuid(saved.getUuid());
assertThat(found).hasValue(saved); assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved);
} }
} }
@ -102,7 +104,7 @@ class CustomerRepositoryIntegrationTest {
currentUser("mike@hostsharing.net"); currentUser("mike@hostsharing.net");
// when // when
final var result = customerRepository.findAll(); final var result = customerRepository.findCustomerByOptionalPrefix(null);
// then // then
exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac");
@ -115,7 +117,7 @@ class CustomerRepositoryIntegrationTest {
assumedRoles("global#hostsharing.admin"); assumedRoles("global#hostsharing.admin");
// when // when
final var result = customerRepository.findAll(); final var result = customerRepository.findCustomerByOptionalPrefix(null);
then: then:
exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac");
@ -127,7 +129,7 @@ class CustomerRepositoryIntegrationTest {
currentUser("admin@aaa.example.com"); currentUser("admin@aaa.example.com");
// when: // when:
final var result = customerRepository.findAll(); final var result = customerRepository.findCustomerByOptionalPrefix(null);
// then: // then:
exactlyTheseCustomersAreReturned(result, "aaa"); exactlyTheseCustomersAreReturned(result, "aaa");
@ -138,7 +140,7 @@ class CustomerRepositoryIntegrationTest {
currentUser("admin@aaa.example.com"); currentUser("admin@aaa.example.com");
assumedRoles("package#aaa00.admin"); assumedRoles("package#aaa00.admin");
final var result = customerRepository.findAll(); final var result = customerRepository.findCustomerByOptionalPrefix(null);
exactlyTheseCustomersAreReturned(result, "aaa"); exactlyTheseCustomersAreReturned(result, "aaa");
} }
@ -152,7 +154,7 @@ class CustomerRepositoryIntegrationTest {
// when // when
final var attempt = attempt( final var attempt = attempt(
em, em,
() -> customerRepository.findAll()); () -> customerRepository.findCustomerByOptionalPrefix(null));
// then // then
attempt.assertExceptionWithRootCauseMessage( attempt.assertExceptionWithRootCauseMessage(
@ -166,7 +168,7 @@ class CustomerRepositoryIntegrationTest {
final var attempt = attempt( final var attempt = attempt(
em, em,
() -> customerRepository.findAll()); () -> customerRepository.findCustomerByOptionalPrefix(null));
attempt.assertExceptionWithRootCauseMessage( attempt.assertExceptionWithRootCauseMessage(
JpaSystemException.class, JpaSystemException.class,
@ -181,7 +183,7 @@ class CustomerRepositoryIntegrationTest {
final var attempt = attempt( final var attempt = attempt(
em, em,
() -> customerRepository.findAll()); () -> customerRepository.findCustomerByOptionalPrefix(null));
attempt.assertExceptionWithRootCauseMessage( attempt.assertExceptionWithRootCauseMessage(
JpaSystemException.class, JpaSystemException.class,
@ -190,6 +192,34 @@ class CustomerRepositoryIntegrationTest {
} }
@Nested
class FindByPrefixLike {
@Test
public void hostsharingAdmin_withoutAssumedRole_canViewAllCustomers() {
// given
currentUser("mike@hostsharing.net");
// when
final var result = customerRepository.findCustomerByOptionalPrefix("aab");
// then
exactlyTheseCustomersAreReturned(result, "aab");
}
@Test
public void customerAdmin_withoutAssumedRole_canViewOnlyItsOwnCustomer() {
// given:
currentUser("admin@aaa.example.com");
// when:
final var result = customerRepository.findCustomerByOptionalPrefix("aab");
// then:
exactlyTheseCustomersAreReturned(result);
}
}
void currentUser(final String currentUser) { void currentUser(final String currentUser) {
context.setCurrentUser(currentUser); context.setCurrentUser(currentUser);
assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser); assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser);