Skip to content

Commit 4faaee4

Browse files
authored
Merge pull request #4 from luismr/hotfix/tests-rely-instant-with-clock
Add Clock Support for Deterministic Timestamps
2 parents 55ce1de + 15ef4ef commit 4faaee4

File tree

7 files changed

+202
-130
lines changed

7 files changed

+202
-130
lines changed

README.md

Lines changed: 129 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,122 @@ A Spring Boot application for tracking flight events.
1818
![Coverage](badges/jacoco.svg)
1919
![Branches](badges/branches.svg)
2020

21-
## Getting Started
21+
## Features
22+
23+
- Real-time flight position tracking
24+
- Kafka-based event streaming
25+
- PostgreSQL database with read-write routing
26+
- Redis caching
27+
- Swagger/OpenAPI documentation
28+
- Configurable timezone support
2229

23-
### Prerequisites
30+
## Prerequisites
2431

25-
- Java 21
26-
- Maven 3.9.6
32+
- Java 17 or later
2733
- Docker and Docker Compose
34+
- PostgreSQL
35+
- Redis
36+
- Kafka
37+
38+
## Configuration
39+
40+
The application can be configured through `application.yml`. Key configurations include:
41+
42+
### Database
43+
44+
```yaml
45+
spring:
46+
datasource:
47+
writer:
48+
jdbcUrl: jdbc:postgresql://localhost:5432/flighttracker
49+
username: flighttracker
50+
password: flighttracker
51+
reader:
52+
jdbcUrl: jdbc:postgresql://localhost:5433/flighttracker
53+
username: flighttracker
54+
password: flighttracker
55+
```
56+
57+
### Kafka
58+
59+
```yaml
60+
spring:
61+
kafka:
62+
bootstrap-servers: localhost:9092
63+
consumer:
64+
group-id: flight-tracker-group
65+
topic:
66+
flight-positions: flight-positions
67+
ping-created: ping-created
68+
```
69+
70+
### Redis
71+
72+
```yaml
73+
spring:
74+
redis:
75+
host: localhost
76+
port: 6379
77+
```
78+
79+
### Clock Configuration
80+
81+
The application uses a configurable clock for timestamp operations. By default, it uses UTC:
82+
83+
```yaml
84+
app:
85+
clock:
86+
timezone: UTC
87+
```
88+
89+
You can change the timezone to any valid timezone ID (e.g., "America/New_York", "Europe/London"):
90+
91+
```yaml
92+
app:
93+
clock:
94+
timezone: America/New_York
95+
```
96+
97+
### API Documentation
98+
99+
Swagger UI is available at `/swagger-ui.html` with the following configuration:
100+
101+
```yaml
102+
springdoc:
103+
api-docs:
104+
path: /api-docs
105+
swagger-ui:
106+
path: /swagger-ui.html
107+
```
108+
109+
## Development
110+
111+
### Running the Application
112+
113+
1. Start the required services using Docker Compose:
114+
```bash
115+
docker-compose up -d
116+
```
117+
118+
2. Run the application:
119+
```bash
120+
./mvnw spring-boot:run
121+
```
122+
123+
### Testing
124+
125+
Run the tests:
126+
```bash
127+
./mvnw test
128+
```
129+
130+
## API Documentation
131+
132+
The API documentation is available at:
133+
- Swagger UI: http://localhost:8080/swagger-ui.html
134+
- OpenAPI JSON: http://localhost:8080/api-docs
135+
136+
## Getting Started
28137

29138
### External Dependencies
30139

@@ -120,12 +229,6 @@ cd flight-tracker-event-server-java
120229
mvn clean install
121230
```
122231

123-
### Running
124-
125-
```bash
126-
mvn spring-boot:run
127-
```
128-
129232
## Contributing
130233

131234
1. Fork the repository
@@ -193,117 +296,22 @@ app:
193296
## Project Structure
194297

195298
```
196-
.
197-
├── .github/
198-
│ └── workflows/
199-
│ └── maven.yml
200-
├── src/
201-
│ ├── main/
202-
│ │ ├── java/
203-
│ │ │ └── dev/
204-
│ │ │ └── luismachadoreis/
205-
│ │ │ └── flighttracker/
206-
│ │ │ └── server/
207-
│ │ │ ├── common/
208-
│ │ │ │ ├── application/
209-
│ │ │ │ │ └── cqs/
210-
│ │ │ │ │ ├── command/
211-
│ │ │ │ │ │ ├── Command.java
212-
│ │ │ │ │ │ └── CommandHandler.java
213-
│ │ │ │ │ ├── query/
214-
│ │ │ │ │ │ ├── Query.java
215-
│ │ │ │ │ │ └── QueryHandler.java
216-
│ │ │ │ │ └── mediator/
217-
│ │ │ │ │ ├── Mediator.java
218-
│ │ │ │ │ └── SpringMediator.java
219-
│ │ │ │ └── infrastructure/
220-
│ │ │ │ ├── datasource/
221-
│ │ │ │ │ ├── DbContextHolder.java
222-
│ │ │ │ │ ├── ReadWriteRoutingAspect.java
223-
│ │ │ │ │ ├── ReadWriteRoutingProperties.java
224-
│ │ │ │ │ └── RoutingDataSource.java
225-
│ │ │ │ ├── DatasourceConfig.java
226-
│ │ │ │ ├── KafkaConfig.java
227-
│ │ │ │ └── OpenApiConfig.java
228-
│ │ │ ├── flightdata/
229-
│ │ │ │ └── infrastructure/
230-
│ │ │ │ ├── kafka/
231-
│ │ │ │ │ └── FlightDataConsumerConfig.java
232-
│ │ │ │ └── pubsub/
233-
│ │ │ │ └── FlightDataSubscriber.java
234-
│ │ │ ├── ping/
235-
│ │ │ │ ├── api/
236-
│ │ │ │ │ └── PingController.java
237-
│ │ │ │ ├── application/
238-
│ │ │ │ │ ├── dto/
239-
│ │ │ │ │ │ ├── FlightDataDTO.java
240-
│ │ │ │ │ │ ├── PingDTO.java
241-
│ │ │ │ │ │ └── PingDTOMapper.java
242-
│ │ │ │ │ └── usecase/
243-
│ │ │ │ │ ├── CreatePingCommand.java
244-
│ │ │ │ │ ├── CreatePingCommandHandler.java
245-
│ │ │ │ │ ├── GetRecentPingsQuery.java
246-
│ │ │ │ │ └── GetRecentPingsQueryHandler.java
247-
│ │ │ │ ├── domain/
248-
│ │ │ │ │ ├── event/
249-
│ │ │ │ │ │ └── PingCreated.java
250-
│ │ │ │ │ ├── Ping.java
251-
│ │ │ │ │ └── PingRepository.java
252-
│ │ │ │ └── infrastructure/
253-
│ │ │ │ ├── pubsub/
254-
│ │ │ │ │ └── ping/
255-
│ │ │ │ │ └── PingEventPublisher.java
256-
│ │ │ │ └── repository/
257-
│ │ │ │ └── JpaPingRepository.java
258-
│ │ │ └── FlightTrackerApplication.java
259-
│ │ └── resources/
260-
│ │ ├── application.yml
261-
│ │ └── application-test.yml
262-
│ └── test/
263-
│ └── java/
264-
│ └── dev/
265-
│ └── luismachadoreis/
266-
│ └── flighttracker/
267-
│ └── server/
268-
│ ├── common/
269-
│ │ ├── application/
270-
│ │ │ └── cqs/
271-
│ │ │ └── mediator/
272-
│ │ │ └── SpringMediatorTest.java
273-
│ │ └── infrastructure/
274-
│ │ └── datasource/
275-
│ │ ├── ReadWriteRoutingAspectTest.java
276-
│ │ └── RoutingDataSourceTest.java
277-
│ ├── flightdata/
278-
│ │ └── infrastructure/
279-
│ │ ├── kafka/
280-
│ │ │ └── FlightDataConsumerConfigTest.java
281-
│ │ └── pubsub/
282-
│ │ └── FlightDataSubscriberTest.java
283-
│ ├── ping/
284-
│ │ ├── api/
285-
│ │ │ └── PingControllerTest.java
286-
│ │ ├── application/
287-
│ │ │ ├── dto/
288-
│ │ │ │ ├── FlightDataDTOTest.java
289-
│ │ │ │ ├── PingDTOTest.java
290-
│ │ │ │ └── PingDTOMapperTest.java
291-
│ │ │ └── usecase/
292-
│ │ │ ├── CreatePingCommandHandlerTest.java
293-
│ │ │ └── GetRecentPingsQueryHandlerTest.java
294-
│ │ └── domain/
295-
│ │ └── PingTest.java
296-
│ └── SpringContextTest.java
297-
├── badges/
298-
│ ├── jacoco.svg
299-
│ └── branches.svg
300-
├── db/
301-
│ └── init-scripts/
302-
│ └── 01-init.sql
303-
├── docker-compose.yml
304-
├── LICENSE.md
305-
├── pom.xml
306-
└── README.md
299+
src/
300+
├── main/
301+
│ ├── java/
302+
│ │ └── dev/luismachadoreis/flighttracker/server/
303+
│ │ ├── common/ # Common infrastructure and utilities
304+
│ │ ├── flightdata/ # Flight data processing
305+
│ │ └── ping/ # Ping domain and API
306+
│ └── resources/
307+
│ ├── application.yml # Main configuration
308+
│ └── application-test.yml # Test configuration
309+
└── test/
310+
└── java/
311+
└── dev/luismachadoreis/flighttracker/server/
312+
├── common/ # Common infrastructure tests
313+
├── flightdata/ # Flight data tests
314+
└── ping/ # Ping domain and API tests
307315
```
308316
309317
## License
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package dev.luismachadoreis.flighttracker.server.common.infrastructure;
2+
3+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
7+
import java.time.Clock;
8+
import java.time.ZoneId;
9+
10+
/**
11+
* Configuration for Clock beans.
12+
*/
13+
@Configuration
14+
@EnableConfigurationProperties(ClockProperties.class)
15+
public class ClockConfig {
16+
17+
/**
18+
* Creates a new Clock bean using the configured timezone.
19+
* @param properties The clock properties
20+
* @return the Clock
21+
*/
22+
@Bean
23+
public Clock clock(ClockProperties properties) {
24+
return Clock.system(ZoneId.of(properties.timezone()));
25+
}
26+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package dev.luismachadoreis.flighttracker.server.common.infrastructure;
2+
3+
import java.util.Objects;
4+
5+
import org.springframework.boot.context.properties.ConfigurationProperties;
6+
7+
/**
8+
* Configuration properties for clock settings.
9+
*/
10+
@ConfigurationProperties(prefix = "app.clock")
11+
public record ClockProperties(
12+
/**
13+
* The timezone to use for the clock.
14+
* Defaults to "UTC" if not specified.
15+
*/
16+
String timezone
17+
) {
18+
public ClockProperties {
19+
timezone = Objects.requireNonNullElse(timezone, "UTC");
20+
}
21+
}

src/main/java/dev/luismachadoreis/flighttracker/server/ping/application/dto/PingMapper.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22

33
import dev.luismachadoreis.flighttracker.server.ping.domain.Ping;
44
import org.springframework.stereotype.Component;
5+
import java.time.Clock;
56
import java.time.Instant;
67
import java.util.UUID;
78

89
@Component
910
public class PingMapper {
1011

12+
private final Clock clock;
13+
14+
public PingMapper(Clock clock) {
15+
this.clock = clock;
16+
}
17+
1118
/**
1219
* Map a Ping to a PingDTO.
1320
* @param ping The Ping to map.
@@ -107,7 +114,7 @@ public PingDTO fromFlightData(FlightDataDTO flightData) {
107114
flightData.positionSource(),
108115
flightData.timePosition() != null ? Instant.ofEpochSecond(flightData.timePosition()) : null
109116
),
110-
Instant.now()
117+
Instant.now(clock)
111118
);
112119
}
113120
}

src/main/resources/application.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,6 @@ api:
9191

9292
app:
9393
read-write-routing:
94-
enabled: false
94+
enabled: false
95+
clock:
96+
timezone: UTC

0 commit comments

Comments
 (0)