Skip to content

Commit

Permalink
Merge pull request #58 from groldan/flyway/modularity_upgrade
Browse files Browse the repository at this point in the history
Add dependency on flyway-database-postgresql after flyway 10.x upgrade
  • Loading branch information
groldan authored Mar 25, 2024
2 parents 525a92e + 1319014 commit 38afb42
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 29 deletions.
25 changes: 22 additions & 3 deletions src/artifacts/api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
Expand All @@ -86,15 +90,30 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.geoserver.acl.openapi.spring5</groupId>
<artifactId>gs-acl-openapi-client</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
Expand Down
2 changes: 1 addition & 1 deletion src/artifacts/api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ server:
spring:
config:
import:
- optional:./values.yml
- classpath:/values.yml
- optional:file:/etc/geoserver/acl-service.yml
main:
banner-mode: off
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* (c) 2023 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.acl.app;

import static org.assertj.core.api.Assertions.assertThat;

import org.geoserver.acl.api.model.GrantType;
import org.geoserver.acl.api.model.Rule;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

import java.util.List;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
abstract class AbstractAccesControlListApplicationTest {

protected final String adminUsername = "admin";
protected final String adminUserPassword = "s3cr3t";

protected final String testUsername = "testuser";
protected final String testUserPassword = "changeme";

@DynamicPropertySource
static void registerTestUser(DynamicPropertyRegistry registry) {
// define a non-admin test user
registry.add("geoserver.acl.security.internal.users.testuser.enabled", () -> "true");
registry.add("geoserver.acl.security.internal.users.testuser.admin", () -> "false");
registry.add("geoserver.acl.security.internal.users.testuser.password", () -> "changeme");
}

@Autowired TestRestTemplate baseClient;

// client to be used, may have login credentials
TestRestTemplate client;

@BeforeEach
void setup() {
client = baseClient;
}

@Test
void getRulesUnauthorizedIfNotLoggedIn() {
var response = get("/api/rules", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}

@Test
void getRulesLoggedInAsNonAdminUser() {
loginAsUser();
var response = get("/api/rules", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}

@Test
void createAllowRule() {
loginAsAdmin();
var response =
createRule(
"""
{
"priority": 0,
"access": "ALLOW"
}
""");
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
assertThat(response.getBody().getPriority()).isPositive();
assertThat(response.getBody().getAccess()).isEqualTo(GrantType.ALLOW);
}

@Test
void createDenyRule() {
loginAsAdmin();
var response =
createRule(
"""
{
"priority": 0,
"access": "DENY"
}
""");
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
assertThat(response.getBody().getPriority()).isPositive();
assertThat(response.getBody().getAccess()).isEqualTo(GrantType.DENY);
}

protected void loginAsAdmin() {
login(adminUsername, adminUserPassword);
}

protected void loginAsUser() {
login(testUsername, testUserPassword);
}

protected void login(String user, String pwd) {
client = client.withBasicAuth(user, pwd);
}

protected <T> ResponseEntity<T> get(String url, Class<T> responseType) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<>(headers);

return client.exchange(url, HttpMethod.GET, entity, responseType);
}

private ResponseEntity<Rule> createRule(String json) {
return post("/api/rules", json, Rule.class);
}

protected <T> ResponseEntity<T> post(
String url, String requestBodyJson, Class<T> responseType, Object... urlVariables) {

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<>(requestBodyJson, headers);

return client.postForEntity(url, entity, responseType, urlVariables);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* (c) 2023 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.acl.app;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@Testcontainers(disabledWithoutDocker = true)
class AccesControlListApplicationIT extends AbstractAccesControlListApplicationTest {

private static final DockerImageName POSTGIS_IMAGE_NAME =
DockerImageName.parse("postgis/postgis").asCompatibleSubstituteFor("postgres");

@Container
static PostgreSQLContainer<?> postgis = new PostgreSQLContainer<>(POSTGIS_IMAGE_NAME);

/** Set up the properties defined in values.yml and used as place-holders in application.yml */
@DynamicPropertySource
static void registerPostgresProperties(DynamicPropertyRegistry registry) {
registry.add("pg.host", () -> postgis.getHost());
registry.add("pg.port", () -> postgis.getFirstMappedPort());
registry.add("pg.db", () -> postgis.getDatabaseName());
registry.add("pg.schema", () -> "acltest");
registry.add("pg.username", postgis::getUsername);
registry.add("pg.password", postgis::getPassword);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* (c) 2023 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.acl.app;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeEach;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.ActiveProfiles;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@ActiveProfiles("dev")
class AccesControlListApplicationTest extends AbstractAccesControlListApplicationTest {

@BeforeEach
void setUp() throws Exception {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,24 @@

import java.io.BufferedReader;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Objects;
import java.util.function.Function;

@RequiredArgsConstructor
public abstract class ApiImplSupport<DTO, T> {
public abstract class ApiImplSupport<D, T> {

private final @NonNull NativeWebRequest nativeRequest;
private final @NonNull Function<T, DTO> toApi;
private final @NonNull Function<DTO, T> toModel;
private final @NonNull Function<T, D> toApi;
private final @NonNull Function<D, T> toModel;

private final RuleFilterApiMapper filterMapper = new RuleFilterApiMapper();

public T toModel(DTO dto) {
public T toModel(D dto) {
return toModel.apply(dto);
}

public DTO toApi(T model) {
public D toApi(T model) {
return toApi.apply(model);
}

Expand All @@ -68,7 +69,7 @@ public org.geoserver.acl.domain.rules.RuleFilter map(RuleFilter filter) {
}

public T mergePatch(final T orig) {
DTO merged;
D merged;
try {
RequestBodyBufferingServletRequest bufferedRequest =
nativeRequest.getNativeRequest(RequestBodyBufferingServletRequest.class);
Expand All @@ -77,14 +78,13 @@ public T mergePatch(final T orig) {
"Servlet Filter not set up, expected RequestBodyBufferingServletRequest");
BufferedReader reader = bufferedRequest.getReader();

DTO current = toApi.apply(orig);
D current = toApi.apply(orig);
ObjectReader readerForUpdating = new ObjectMapper().readerForUpdating(current);
merged = readerForUpdating.readValue(reader);
} catch (IOException e) {
throw new RuntimeException(e);
throw new UncheckedIOException(e);
}
T patched = toModel.apply(merged);
return patched;
return toModel.apply(merged);
}

public <R> ResponseEntity<R> error(HttpStatus code, String reason) {
Expand All @@ -95,8 +95,13 @@ public void setPreferredGeometryEncoding() {
String acceptContentType = nativeRequest.getHeader("Accept");
boolean useWkb = true;
if (StringUtils.hasText(acceptContentType)) {
MediaType mediaType = MediaType.parseMediaType(acceptContentType);
useWkb = !MediaType.APPLICATION_JSON.isCompatibleWith(mediaType);
try {
String contentType = acceptContentType.split(",")[0];
MediaType mediaType = MediaType.parseMediaType(contentType);
useWkb = !MediaType.APPLICATION_JSON.isCompatibleWith(mediaType);
} catch (Exception e) {
useWkb = false;
}
} else {
useWkb = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,19 @@ class RulesApiImpTest {
@Test
void testCreateRule() {
Rule ret = Rule.allow().withId("1");
when(rules.insert(eq(Rule.allow()))).thenReturn(ret);
when(rules.insert(Rule.allow())).thenReturn(ret);

assertResponse(() -> api.createRule(support.toApi(Rule.allow()), null), CREATED, ret);

verify(rules, times(1)).insert(eq(Rule.allow()));
verify(rules, times(1)).insert(Rule.allow());
verifyNoMoreInteractions(rules);
clearInvocations(rules);

when(rules.insert(any(), any()))
.thenThrow(new RuleIdentifierConflictException("Duplicate identifier"));

assertError(create(Rule.deny(), InsertPosition.FROM_END), CONFLICT, "Duplicate identifier");
verify(rules, times(1)).insert(eq(Rule.deny()), eq(InsertPosition.FROM_END));
verify(rules, times(1)).insert(Rule.deny(), InsertPosition.FROM_END);
}

private Supplier<ResponseEntity<org.geoserver.acl.api.model.Rule>> create(
Expand Down Expand Up @@ -105,11 +105,11 @@ void testDeleteRuleById() {
void testGetRules() {
RuleQuery<RuleFilter> expectedQuery = RuleQuery.of();
List<Rule> expected = List.of(Rule.allow(), Rule.deny());
when(rules.getAll(eq(expectedQuery))).thenReturn(expected.stream());
when(rules.getAll(expectedQuery)).thenReturn(expected.stream());

List<Rule> actual = assertList(() -> api.getRules(null, null), OK);
assertThat(actual).isEqualTo(expected);
verify(rules, times(1)).getAll(eq(expectedQuery));
verify(rules, times(1)).getAll(expectedQuery);
}

private List<Rule> assertList(
Expand All @@ -132,8 +132,8 @@ void testQueryRules() {
@Test
void testGetRuleById() {
Rule found = Rule.allow().withId("id1");
when(rules.get(eq("id1"))).thenReturn(Optional.of(found));
when(rules.get(eq("id2"))).thenReturn(Optional.empty());
when(rules.get("id1")).thenReturn(Optional.of(found));
when(rules.get("id2")).thenReturn(Optional.empty());

assertResponse(() -> api.getRuleById("id1"), OK, found);
assertResponse(() -> api.getRuleById("id2"), NOT_FOUND, null);
Expand All @@ -142,8 +142,8 @@ void testGetRuleById() {
@Test
void testFindOneRuleByPriority() {
Rule found = Rule.allow().withId("id1");
when(rules.getRuleByPriority(eq(1000L))).thenReturn(Optional.of(found));
when(rules.getRuleByPriority(eq(1001L))).thenReturn(Optional.empty());
when(rules.getRuleByPriority(1000L)).thenReturn(Optional.of(found));
when(rules.getRuleByPriority(1001L)).thenReturn(Optional.empty());

assertResponse(() -> api.findOneRuleByPriority(1000L), OK, found);
assertResponse(() -> api.findOneRuleByPriority(1001L), NOT_FOUND, null);
Expand All @@ -166,8 +166,8 @@ void testCountRules() {
@Test
void testRuleExistsById() {
Rule found = Rule.allow().withId("id1");
when(rules.get(eq("id1"))).thenReturn(Optional.of(found));
when(rules.get(eq("id2"))).thenReturn(Optional.empty());
when(rules.get("id1")).thenReturn(Optional.of(found));
when(rules.get("id2")).thenReturn(Optional.empty());

assertThat(api.ruleExistsById("id1").getStatusCode()).isEqualByComparingTo(OK);
assertThat(api.ruleExistsById("id1").getBody()).isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class JpaAdminRuleRepositoryPostGIS_IT extends JpaAdminRuleRepositoryTest {
static PostgreSQLContainer<?> postgis = new PostgreSQLContainer<>(POSTGIS_IMAGE_NAME);

@DynamicPropertySource
static void registerMySQLProperties(DynamicPropertyRegistry registry) {
static void registerPostgresProperties(DynamicPropertyRegistry registry) {
registry.add("geoserver.acl.datasource.url", () -> postgis.getJdbcUrl());
registry.add("geoserver.acl.datasource.username", postgis::getUsername);
registry.add("geoserver.acl.datasource.password", postgis::getPassword);
Expand Down
Loading

0 comments on commit 38afb42

Please sign in to comment.