Skip to content

Commit

Permalink
feat: support filter parameter for rest findAll interface
Browse files Browse the repository at this point in the history
  • Loading branch information
dnlkoch committed Sep 4, 2023
1 parent e275bc6 commit e070e91
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,21 @@
package de.terrestris.shogun.boot.controller;

import de.terrestris.shogun.lib.controller.ApplicationController;
import de.terrestris.shogun.lib.enumeration.PermissionCollectionType;
import de.terrestris.shogun.lib.model.Application;
import de.terrestris.shogun.lib.repository.ApplicationRepository;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.Matchers.hasSize;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

public class ApplicationControllerTest extends BaseControllerTest<ApplicationController, ApplicationRepository, Application> {

public void setBaseEntity() {
Expand Down Expand Up @@ -53,4 +62,56 @@ public void insertTestData() {
testData = persistedEntities;
}

@Test
public void findAll_shouldReturnFilteredEntitiesForAdminUsers() throws Exception {
this.mockMvc
.perform(
MockMvcRequestBuilders
.get(basePath + "?filter=$.name == \"Application 1\"")
.with(authentication(getMockAuthentication(this.adminUser)))
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$").isMap())
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content", hasSize(1)));
}

@Test
public void findAll_shouldReturnFilteredEntitiesForUsersByUserInstancePermission() throws Exception {

userInstancePermissionService.setPermission(testData.get(0), this.user, PermissionCollectionType.READ);
userInstancePermissionService.setPermission(testData.get(1), this.user, PermissionCollectionType.READ);

this.mockMvc
.perform(
MockMvcRequestBuilders
.get(basePath + "?filter=$.name == \"Application 1\"")
.with(authentication(getMockAuthentication(this.user)))
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$").isMap())
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content", hasSize(1)));
}

@Test
public void findAll_shouldReturnFilteredEntitiesForUsersByGroupInstancePermission() throws Exception {

groupInstancePermissionService.setPermission(testData.get(0), this.group, PermissionCollectionType.READ);
groupInstancePermissionService.setPermission(testData.get(1), this.group, PermissionCollectionType.READ);

this.mockMvc
.perform(
MockMvcRequestBuilders
.get(basePath + "?filter=$.name == \"Application 1\"")
.with(authentication(getMockAuthentication(this.user)))
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$").isMap())
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content", hasSize(1)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,28 @@
import de.terrestris.shogun.lib.controller.BaseController;
import de.terrestris.shogun.lib.enumeration.PermissionCollectionType;
import de.terrestris.shogun.lib.model.BaseEntity;
import de.terrestris.shogun.lib.model.Group;
import de.terrestris.shogun.lib.model.User;
import de.terrestris.shogun.lib.repository.BaseCrudRepository;
import de.terrestris.shogun.lib.repository.GroupRepository;
import de.terrestris.shogun.lib.repository.UserRepository;
import de.terrestris.shogun.lib.repository.security.permission.GroupClassPermissionRepository;
import de.terrestris.shogun.lib.repository.security.permission.GroupInstancePermissionRepository;
import de.terrestris.shogun.lib.repository.security.permission.UserClassPermissionRepository;
import de.terrestris.shogun.lib.repository.security.permission.UserInstancePermissionRepository;
import de.terrestris.shogun.lib.service.security.permission.GroupInstancePermissionService;
import de.terrestris.shogun.lib.service.security.permission.UserClassPermissionService;
import de.terrestris.shogun.lib.service.security.permission.UserInstancePermissionService;
import de.terrestris.shogun.lib.service.security.provider.GroupProviderService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.web.servlet.server.Encoding;
import org.springframework.http.MediaType;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
Expand All @@ -57,6 +65,7 @@
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
Expand All @@ -79,15 +88,27 @@ public abstract class BaseControllerTest<U extends BaseController, R extends Bas
@Autowired
protected UserRepository userRepository;

@Autowired
protected GroupRepository groupRepository;

@Autowired
protected UserInstancePermissionRepository userInstancePermissionRepository;

@Autowired
protected UserClassPermissionRepository userClassPermissionRepository;

@Autowired
protected GroupInstancePermissionRepository groupInstancePermissionRepository;

@Autowired
protected GroupClassPermissionRepository groupClassPermissionRepository;

@Autowired
protected UserInstancePermissionService userInstancePermissionService;

@Autowired
protected GroupInstancePermissionService groupInstancePermissionService;

@Autowired
protected UserClassPermissionService userClassPermissionService;

Expand All @@ -97,6 +118,9 @@ public abstract class BaseControllerTest<U extends BaseController, R extends Bas
@Autowired
protected ObjectMapper objectMapper;

@MockBean
private GroupProviderService groupProviderService;

protected Class<S> entityClass;

protected String basePath;
Expand All @@ -109,6 +133,8 @@ public abstract class BaseControllerTest<U extends BaseController, R extends Bas

protected User user;

protected Group group;

public void initMockMvc() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(context)
Expand Down Expand Up @@ -149,9 +175,25 @@ public void initUser() {
this.user = userRepository.save(user);
}

public void initGroup() {
Group<GroupRepresentation> group = new Group();
String authProviderId = "c886684b-1a22-4443-b0d8-36c006c19f08";
group.setAuthProviderId(authProviderId);
GroupRepresentation groupRepresentation = new GroupRepresentation();
groupRepresentation.setName("SHOGun Group");
group.setProviderDetails(groupRepresentation);

this.group = groupRepository.save(group);

when(groupProviderService.getGroupsForUser()).thenReturn(List.of(this.group));
when(groupProviderService.findByUser(this.user)).thenReturn(List.of(this.group));
}

public void cleanupPermissions() {
userInstancePermissionRepository.deleteAll();
groupInstancePermissionRepository.deleteAll();
userClassPermissionRepository.deleteAll();
groupClassPermissionRepository.deleteAll();
}

public void deinitAdminUser() {
Expand All @@ -162,6 +204,10 @@ public void deinitUser() {
userRepository.delete(this.user);
}

public void deinitGroup() {
groupRepository.delete(this.group);
}

public void cleanupTestData() {
repository.deleteAll();
}
Expand Down Expand Up @@ -192,6 +238,7 @@ public void setUp() {
initMockMvc();
initAdminUser();
initUser();
initGroup();
setBaseEntity();
setBasePath();
insertTestData();
Expand All @@ -202,6 +249,7 @@ public void teardown() {
cleanupPermissions();
deinitUser();
deinitAdminUser();
deinitGroup();
cleanupTestData();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@
package de.terrestris.shogun.lib.controller;

import com.github.fge.jsonpatch.mergepatch.JsonMergePatch;
import com.jayway.jsonpath.Filter;
import de.terrestris.shogun.lib.controller.security.permission.BasePermissionController;
import de.terrestris.shogun.lib.model.BaseEntity;
import de.terrestris.shogun.lib.service.BaseService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
Expand Down Expand Up @@ -64,6 +68,34 @@ public abstract class BaseController<T extends BaseService<?, S>, S extends Base
summary = "Returns all entities",
security = { @SecurityRequirement(name = "bearer-key") }
)
@Parameters(
@Parameter(
name = "filter",
description = "JSONPath predicate, see https://github.com/json-path/JsonPath#predicates",
examples = {
@ExampleObject(
name = "Example 1",
description = "Get all entities with name \"Countries\"",
value = "$.name == \"Countries\""
),
@ExampleObject(
name = "Example 2",
description = "Get all hoverable layers",
value = "$.clientConfig.hoverable == true"
),
@ExampleObject(
name = "Example 3",
description = "Get all layers with a greater minResolution of 100 and of type TILEWMS",
value = "$.clientConfig.minResolution > 100 && $.type == \"TILEWMS\""
),
@ExampleObject(
name = "Example 4",
description = "Get all entities with the names \"Countries\" or \"World Map\"",
value = "$.name == \"Countries\" || $.name == \"World Map\""
)
}
)
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
Expand All @@ -80,14 +112,21 @@ public abstract class BaseController<T extends BaseService<?, S>, S extends Base
),
@ApiResponse(
responseCode = "500",
description = "Internal Server Error: Something internal went wrong while deleting the entity"
description = "Internal Server Error: Something internal went wrong while getting the entity list"
)
})
public Page<S> findAll(@PageableDefault(Integer.MAX_VALUE) @ParameterObject Pageable pageable) {
public Page<S> findAll(@PageableDefault(Integer.MAX_VALUE) @ParameterObject Pageable pageable, @RequestParam(required = false) String filter) {
log.trace("Requested to return all entities of type {}", getGenericClassName());

try {
Page<S> persistedEntities = service.findAll(pageable);
Filter compiledFilter = null;
if (StringUtils.isNotEmpty(filter)) {
compiledFilter = Filter.parse(String.format("[?(%s)]", filter));

log.trace("Got filter " + compiledFilter.toString().replace("'", "\""));
}

Page<S> persistedEntities = service.findAll(pageable, compiledFilter);

log.trace("Successfully got all entities of type {} (count: {})",
getGenericClassName(), persistedEntities.getTotalElements());
Expand Down
Loading

0 comments on commit e070e91

Please sign in to comment.