Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions auditlog/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
4 changes: 0 additions & 4 deletions auditlog/src/main/resources/application.properties

This file was deleted.

29 changes: 18 additions & 11 deletions auditlog/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
server:
port: 8089
spring:
application:
name: auditlog-service
rabbitmq:
addresses: rabbitmq:5672
username: shiftcontrol
password: password
jpa:
properties:
hibernate:
type:
json_format_mapper: jsonb
application:
name: auditlog-service
datasource:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
mvc:
servlet:
path: ${AUDITLOG_REST_PATH_PREFIX:/}
rabbitmq:
addresses: rabbitmq:5672
username: shiftcontrol
password: password
jpa:
properties:
hibernate:
type:
json_format_mapper: jsonb
keycloak:
auth-server-url: http://keycloak.127.0.0.1.nip.io/
auth-realm: dev
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package at.shiftcontrol.auditlog.endpoint;

import java.time.Instant;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.security.oauth2.server.resource.autoconfigure.servlet.OAuth2ResourceServerAutoConfiguration;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.test.web.servlet.MockMvc;
import org.junit.jupiter.api.Test;

import at.shiftcontrol.auditlog.dto.LogEntryDto;
import at.shiftcontrol.auditlog.dto.LogSearchDto;
import at.shiftcontrol.auditlog.service.AuditLogService;
import at.shiftcontrol.lib.dto.PaginationDto;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(
controllers = LogEndpoint.class,
properties = "spring.mvc.servlet.path=/auditlog",
excludeAutoConfiguration = OAuth2ResourceServerAutoConfiguration.class
)
@AutoConfigureMockMvc(addFilters = false)
@Import(LogEndpointPathPrefixTest.TestConfig.class)
class LogEndpointPathPrefixTest {

@Autowired
private MockMvc mockMvc;

@Value("${spring.mvc.servlet.path}")
private String servletPath;

@Test
void shouldExposeEndpointUnderConfiguredPrefix() throws Exception {
org.junit.jupiter.api.Assertions.assertEquals("/auditlog", servletPath);

mockMvc.perform(get("/auditlog/api/v1/log")
.servletPath("/auditlog")
.param("page", "0")
.param("size", "10")
.param("startTime", Instant.parse("2026-01-01T00:00:00Z").toString())
.param("endTime", Instant.parse("2026-01-02T00:00:00Z").toString()))
.andExpect(status().isOk());
}

@TestConfiguration
static class TestConfig {
@Bean
@Primary
AuditLogService auditLogService() {
return new AuditLogService(null) {
@Override
public PaginationDto<LogEntryDto> search(int page, int size, LogSearchDto searchDto) {
return PaginationDto.<LogEntryDto>builder()
.page(page)
.pages(0)
.total(0)
.items(List.of())
.build();
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package at.shiftcontrol.auditlog.endpoint;

import java.time.Instant;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.security.oauth2.server.resource.autoconfigure.servlet.OAuth2ResourceServerAutoConfiguration;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.test.web.servlet.MockMvc;
import org.junit.jupiter.api.Test;

import at.shiftcontrol.auditlog.dto.LogEntryDto;
import at.shiftcontrol.auditlog.dto.LogSearchDto;
import at.shiftcontrol.auditlog.service.AuditLogService;
import at.shiftcontrol.lib.dto.PaginationDto;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(
controllers = LogEndpoint.class,
properties = "spring.mvc.servlet.path=/",
excludeAutoConfiguration = OAuth2ResourceServerAutoConfiguration.class
)
@AutoConfigureMockMvc(addFilters = false)
@Import(LogEndpointRootServletPathTest.TestConfig.class)
class LogEndpointRootServletPathTest {

@Autowired
private MockMvc mockMvc;

@Value("${spring.mvc.servlet.path}")
private String servletPath;

@Test
void shouldExposeEndpointWithoutAdditionalPrefix() throws Exception {
org.junit.jupiter.api.Assertions.assertEquals("/", servletPath);

mockMvc.perform(get("/api/v1/log")
.param("page", "0")
.param("size", "10")
.param("startTime", Instant.parse("2026-01-01T00:00:00Z").toString())
.param("endTime", Instant.parse("2026-01-02T00:00:00Z").toString()))
.andExpect(status().isOk());
}

@TestConfiguration
static class TestConfig {
@Bean
@Primary
AuditLogService auditLogService() {
return new AuditLogService(null) {
@Override
public PaginationDto<LogEntryDto> search(int page, int size, LogSearchDto searchDto) {
return PaginationDto.<LogEntryDto>builder()
.page(page)
.pages(0)
.total(0)
.items(List.of())
.build();
}
};
}
}
}
65 changes: 65 additions & 0 deletions shiftservice/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Shiftservice

## Runtime configuration

`shiftservice` expects these environment variables in normal runtime:

- `SPRING_DATASOURCE_URL`
- `SPRING_DATASOURCE_USERNAME`
- `SPRING_DATASOURCE_PASSWORD`

Optional service-specific variables:

- `SHIFTSERVICE_REST_PATH_PREFIX`
- Defaults to `/`, which keeps endpoints at `/api/v1/...` with no extra prefix.
- Example: set `/shiftservice` to expose endpoints as `/shiftservice/api/v1/...`.
- `SPRING_LIQUIBASE_CONTEXTS`
- Defaults to `dev`.
- Controls which Liquibase context-tagged change sets are applied.

## Database and Liquibase

Main configuration lives in [application.yml](./src/main/resources/application.yml).

The service runs Hibernate with `ddl-auto=validate`, so schema creation and migration are handled by Liquibase, not Hibernate.

Liquibase is enabled by default and uses:

- changelog: `classpath:db/changelog-master.xml`
- default context: `dev`

## Environments

### Development

Use:

- `SPRING_LIQUIBASE_CONTEXTS=dev` or leave it unset

Effect:

- schema migrations run
- development seed data in `db/data/*` runs because those change sets are tagged with `context="dev"`

### Production

Use:

- `SPRING_LIQUIBASE_CONTEXTS=prod`

Effect:

- schema migrations still run
- `dev` seed data is skipped

This matters because the seed data files under `db/data/` are marked with the `dev` Liquibase context.

## OpenAPI profile

The `openapi` profile uses [application-openapi.yml](./src/main/resources/application-openapi.yml):

- in-memory H2 datasource
- Liquibase disabled
- Hibernate `ddl-auto=none`

That profile is intended only for OpenAPI generation, not normal app runtime.
11 changes: 11 additions & 0 deletions shiftservice/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
Expand Down Expand Up @@ -122,6 +127,12 @@
</build>

<profiles>
<profile>
<id>unit-tests</id>
<properties>
<skipITs>true</skipITs>
</properties>
</profile>

<profile>
<id>openapi</id>
Expand Down
12 changes: 0 additions & 12 deletions shiftservice/src/main/resources/application.properties

This file was deleted.

16 changes: 16 additions & 0 deletions shiftservice/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
spring:
datasource:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
rabbitmq:
addresses: rabbitmq:5672
username: shiftcontrol
password: password
jackson:
default-property-inclusion: non_null
mvc:
servlet:
path: ${SHIFTSERVICE_REST_PATH_PREFIX:/}
jpa:
database: postgresql
database-platform: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: validate
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://keycloak.127.0.0.1.nip.io/realms/dev
liquibase:
change-log: classpath:db/changelog-master.xml
enabled: true
contexts: ${SPRING_LIQUIBASE_CONTEXTS:dev}
keycloak:
auth-server-url: http://keycloak.127.0.0.1.nip.io/
auth-realm: dev
Expand Down
Loading
Loading