Skip to content

Commit 8cd8b3e

Browse files
committed
Update event-store with some additional functionality | Testing the test (C) whatthecommit.com
1 parent a0986be commit 8cd8b3e

File tree

9 files changed

+223
-113
lines changed

9 files changed

+223
-113
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ jobs:
8686
8787
- http :8080/events/00000000-0000-0000-0000-000000000001/collection Accept:application/json
8888
- http :8080/events/00000000-0000-0000-0000-000000000000 Accept:application/json
89+
- http :8080/events
90+
- http delete :8080/events
8991
- stop_any 80 8080
9092

9193
- stage: test
@@ -133,6 +135,8 @@ jobs:
133135
134136
- http :8080/events/00000000-0000-0000-0000-000000000001/collection Accept:application/json
135137
- http :8080/events/00000000-0000-0000-0000-000000000000 Accept:application/json
138+
- http :8080/events
139+
- http delete :8080/events
136140
- stop_any 80 8080
137141

138142
- stage: test

api.http

Lines changed: 20 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,3 @@
1-
POST http://127.0.0.1:8080/events
2-
Content-Type: application/json
3-
Accept: application/json
4-
5-
{
6-
"aggregateId": "00000000-0000-0000-0000-000000000000",
7-
"counterName": "hello",
8-
"type": "CounterCreated"
9-
}
10-
11-
###
12-
POST http://127.0.0.1:8080/events/one
13-
Content-Type: application/json
14-
Accept: application/json
15-
16-
{
17-
"aggregateId": "00000000-0000-0000-0000-000000000000"
18-
}
19-
20-
###
21-
POST http://127.0.0.1:8080/events/two
22-
Content-Type: application/json
23-
Accept: application/json
24-
25-
{
26-
"aggregateId": "00000000-0000-0000-0000-000000000000"
27-
}
28-
29-
###
30-
POST http://127.0.0.1:8080/events/two
31-
Content-Type: application/json
32-
Accept: application/json
33-
34-
{
35-
"aggregateId": "00000000-0000-0000-0000-000000000000",
36-
"data1": "d a t a 1",
37-
"data2": "d a t a 2"
38-
}
39-
40-
###
41-
POST http://127.0.0.1:8080/events/three
42-
Content-Type: application/json
43-
Accept: application/json
44-
45-
{
46-
"aggregateId": "00000000-0000-0000-0000-000000000000"
47-
}
48-
49-
###
50-
POST http://127.0.0.1:8080/events/three
51-
Content-Type: application/json
52-
Accept: application/json
53-
54-
{
55-
"aggregateId": "00000000-0000-0000-0000-000000000000",
56-
"data1": "d-a-t-a-1",
57-
"data2": "d-a-t-a-2"
58-
}
59-
60-
###
61-
POST http://127.0.0.1:8080/events/three
62-
Content-Type: application/json
63-
Accept: application/json
64-
65-
{
66-
"aggregateId": "00000000-0000-0000-0000-000000000000",
67-
"data1": "data 1",
68-
"data2": "data 2",
69-
"data3": "data 3"
70-
}
71-
72-
###
73-
GET http://127.0.0.1:8080/events/00000000-0000-0000-0000-000000000000
74-
Accept: application/json
75-
76-
###
77-
GET http://127.0.0.1:8080/events/00000000-0000-0000-0000-000000000000/collection
78-
Accept: application/json
79-
80-
###
811
POST http://127.0.0.1:8080/events/collection
822
Content-Type: application/json
833
Accept: application/json
@@ -121,3 +41,23 @@ GET http://127.0.0.1:8080/events/00000000-0000-0000-0000-000000000000/collection
12141
Accept: application/json
12242

12343
###
44+
DELETE http://127.0.0.1:8080/events
45+
Accept: application/json
46+
47+
###
48+
POST http://127.0.0.1:8080/events
49+
Content-Type: application/json
50+
Accept: application/json
51+
52+
{
53+
"aggregateId": "00000000-0000-0000-0000-000000000000",
54+
"counterName": "hello",
55+
"type": "CounterCreated"
56+
}
57+
58+
###
59+
GET http://127.0.0.1:8080/events
60+
Content-Type: application/json
61+
Accept: application/json
62+
63+
###

pom.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@
2525
<jersey.version>2.29</jersey.version>
2626
<log4j.version>2.12.1</log4j.version>
2727
<lombok.version>1.18.8</lombok.version>
28+
<assertj.version>3.13.2</assertj.version>
2829
<jandex.version>2.1.1.Final</jandex.version>
2930
<jackson.version>2.10.0.pr2</jackson.version>
31+
<junit-jupiter.version>5.5.1</junit-jupiter.version>
3032
<exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
3133
<jandex-maven-plugin.version>1.0.6</jandex-maven-plugin.version>
3234
<capsule-maven-plugin.version>1.5.1</capsule-maven-plugin.version>
3335
<spotbugs-maven-plugin.version>3.1.12.2</spotbugs-maven-plugin.version>
36+
<maven-surefire-plugin.version>3.0.0-M3</maven-surefire-plugin.version>
3437
</properties>
3538

3639
<dependencyManagement>
@@ -56,6 +59,13 @@
5659
<scope>import</scope>
5760
<type>pom</type>
5861
</dependency>
62+
<dependency>
63+
<groupId>org.junit</groupId>
64+
<artifactId>junit-bom</artifactId>
65+
<version>${junit-jupiter.version}</version>
66+
<scope>import</scope>
67+
<type>pom</type>
68+
</dependency>
5969
</dependencies>
6070
</dependencyManagement>
6171

@@ -121,12 +131,27 @@
121131
<artifactId>vavr</artifactId>
122132
<version>${vavr.version}</version>
123133
</dependency>
134+
<dependency>
135+
<groupId>org.junit.jupiter</groupId>
136+
<artifactId>junit-jupiter</artifactId>
137+
<scope>test</scope>
138+
</dependency>
139+
<dependency>
140+
<groupId>org.assertj</groupId>
141+
<artifactId>assertj-core</artifactId>
142+
<version>${assertj.version}</version>
143+
</dependency>
124144
</dependencies>
125145

126146
<build>
127147
<defaultGoal>clean package</defaultGoal>
128148

129149
<plugins>
150+
<plugin>
151+
<groupId>org.apache.maven.plugins</groupId>
152+
<artifactId>maven-surefire-plugin</artifactId>
153+
<version>${maven-surefire-plugin.version}</version>
154+
</plugin>
130155
<!-- CDI index -->
131156
<plugin>
132157
<groupId>org.jboss.jandex</groupId>

src/main/java/daggerok/eventstore/EventStore.java

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,32 @@
1010
import javax.annotation.PostConstruct;
1111
import javax.enterprise.context.ApplicationScoped;
1212
import javax.inject.Inject;
13-
import java.nio.file.Files;
14-
import java.nio.file.LinkOption;
15-
import java.nio.file.Path;
16-
import java.nio.file.Paths;
17-
import java.util.Collection;
18-
import java.util.Objects;
19-
import java.util.UUID;
13+
import java.nio.file.*;
14+
import java.util.*;
2015
import java.util.concurrent.CopyOnWriteArrayList;
16+
import java.util.function.Predicate;
2117
import java.util.stream.Collectors;
2218
import java.util.stream.Stream;
2319

24-
import static java.nio.charset.StandardCharsets.UTF_8;
2520
import static java.nio.file.StandardOpenOption.APPEND;
2621
import static java.util.Collections.singletonList;
2722

2823
@Log4j2
2924
@ApplicationScoped
3025
public class EventStore {
3126

32-
@Inject
3327
private Path dbBasePath;
28+
private ObjectMapper objectMapper;
29+
30+
EventStore() {} // blah..
3431

3532
@Inject
36-
private ObjectMapper objectMapper;
33+
public EventStore(Path dbBasePath, ObjectMapper objectMapper) {
34+
this.dbBasePath = dbBasePath;
35+
this.objectMapper = objectMapper;
36+
}
37+
38+
private static final String suffix = ".db.json.log";
3739

3840
// Keep in mind: PostConstruct (if needed) happens earlier then ContainerInitialized event will occur, but!
3941
// PostConstruct is Lazy, while ContainerInitialized event will be eventually produced during application bootstrap
@@ -56,7 +58,7 @@ public void postConstruct() {
5658
// step 4: collect and return new CopyOnWriteArrayList with events found.
5759
public Collection<DomainEvent> read(UUID aggregateId) {
5860
log.debug(aggregateId);
59-
Path logFileAbsolutePath = getLogFilenamePath(aggregateId);
61+
Path logFileAbsolutePath = getDbFilePath(aggregateId);
6062
if (Files.notExists(logFileAbsolutePath, LinkOption.NOFOLLOW_LINKS)) return new CopyOnWriteArrayList<>();
6163
@Cleanup Stream<String> jsonStream = Try.of(() -> Files.lines(logFileAbsolutePath))
6264
.getOrElseThrow(this::reThrow)
@@ -71,29 +73,74 @@ public Collection<DomainEvent> read(UUID aggregateId) {
7173
// step 2: convert domainEvent object into JSON string
7274
// step 3: if json doesn't contains type field, throw an exception
7375
// step 4: append JSON string into end of the "${domainEvent.getAggregateId()}.db.json.log" file
74-
public void append(DomainEvent... domainEvents) {
76+
public void appendAll(DomainEvent... domainEvents) {
7577
for (DomainEvent domainEvent : domainEvents) {
76-
log.debug(domainEvent);
78+
append(domainEvent);
79+
}
80+
}
7781

78-
Path logFileAbsolutePath = getLogFilenamePath(domainEvent.getAggregateId());
79-
if (Files.notExists(logFileAbsolutePath, LinkOption.NOFOLLOW_LINKS)) {
80-
Try.run(() -> Files.createFile(logFileAbsolutePath))
81-
.getOrElseThrow(this::reThrow);
82-
}
83-
String json = Try.of(() -> objectMapper.writeValueAsString(domainEvent))
84-
.getOrElseThrow(this::reThrow);
85-
JsonNode jsonNode = Try.of(() -> objectMapper.readTree(json))
86-
.getOrElseThrow(this::reThrow);
87-
if (Objects.nonNull(jsonNode.get("type").asText())) {
88-
Try.run(() -> Files.write(logFileAbsolutePath, singletonList(json), UTF_8, APPEND));
89-
}
82+
public void append(DomainEvent domainEvent) {
83+
log.debug(domainEvent);
84+
85+
Path logFileAbsolutePath = getDbFilePath(domainEvent.getAggregateId());
86+
if (Files.notExists(logFileAbsolutePath, LinkOption.NOFOLLOW_LINKS)) {
87+
Try.run(() -> Files.createFile(logFileAbsolutePath))
88+
.getOrElseThrow(this::reThrow);
9089
}
90+
String json = Try.of(() -> objectMapper.writeValueAsString(domainEvent))
91+
.getOrElseThrow(this::reThrow);
92+
JsonNode jsonNode = Try.of(() -> objectMapper.readTree(json))
93+
.getOrElseThrow(this::reThrow);
94+
if (Objects.nonNull(jsonNode.get("type").asText())) {
95+
Try.run(() -> Files.write(logFileAbsolutePath, singletonList(json), APPEND));
96+
}
97+
}
98+
99+
// - stream all absolute db paths in db folder
100+
// - remove if any exists
101+
public void cleanupAll() {
102+
cleanupBy(entry -> entry.toString().endsWith(suffix));
103+
}
104+
105+
public void cleanupBy(DirectoryStream.Filter<Path> pathFilter) {
106+
log.debug("cleanup");
107+
Try.run(() -> {
108+
try (DirectoryStream<Path> paths = Files.newDirectoryStream(dbBasePath.toAbsolutePath(), pathFilter)) {
109+
for (Path path : paths) {
110+
log.debug("trying remove {} file from event store", path);
111+
Files.deleteIfExists(path);
112+
}
113+
}
114+
}).onFailure(e -> log.error(e.getLocalizedMessage(), e));
91115
}
92116

93-
private Path getLogFilenamePath(UUID aggregateId) {
117+
// - find all files in db folder with suffixed filter
118+
// - take all it's filenames
119+
// - remove suffix part from each filename
120+
// - map it into UUID
121+
// - collect result into list
122+
public Collection<UUID> findAll() {
123+
return findAllBy(filename -> filename.endsWith(suffix));
124+
}
125+
126+
public Collection<UUID> findAllBy(Predicate<String> filenamePredicate) {
127+
String[] files = dbBasePath.toAbsolutePath().toFile().list(
128+
(dir, filename) -> filenamePredicate.test(filename));
129+
log.info("found filenames: {}", files);
130+
return Optional.ofNullable(files)
131+
.map(Arrays::stream)
132+
.orElse(Stream.empty())
133+
.map(filename -> filename.replace(suffix, ""))
134+
.map(UUID::fromString)
135+
.collect(Collectors.toList());
136+
}
137+
138+
/* Private API */
139+
140+
private Path getDbFilePath(UUID aggregateId) {
94141
Objects.requireNonNull(aggregateId);
95-
String logFilename = String.format("%s.db.json.log", aggregateId.toString());
96-
String absoluteParentPath = dbBasePath.toAbsolutePath().toFile().getAbsolutePath();
142+
String logFilename = String.format("%s%s", aggregateId.toString(), suffix);
143+
String absoluteParentPath = dbBasePath.toAbsolutePath().toString();
97144
return Paths.get(absoluteParentPath, logFilename);
98145
}
99146

src/main/java/daggerok/eventstore/EventStoreResource.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,16 @@
2424
@ApplicationScoped
2525
public class EventStoreResource {
2626

27-
@Inject
28-
EventStore eventStore;
27+
private UriInfo uriInfo;
28+
private EventStore eventStore;
29+
30+
EventStoreResource() {} // yuk...
2931

30-
@Context
31-
UriInfo uriInfo;
32+
@Inject
33+
public EventStoreResource(@Context UriInfo uriInfo, EventStore eventStore) {
34+
this.uriInfo = uriInfo;
35+
this.eventStore = eventStore;
36+
}
3237

3338
private BiFunction<Object, String, Object> require = (variable, variableName) ->
3439
Optional.ofNullable(variable).orElseThrow(() -> new IllegalArgumentException(
@@ -103,12 +108,29 @@ public Response getCounterCollection(Collection<DomainEvent> events) {
103108
require.apply(events, "events is require");
104109
for (DomainEvent domainEvent : events) {
105110
require.apply(domainEvent.getAggregateId(), "aggregateId is require");
106-
eventStore.append(domainEvent);
107111
}
112+
// eventStore.appendAll(events.toArray(new DomainEvent[0]));
108113
return Response.accepted()
109114
.entity(Json.createObjectBuilder()
110115
.add("result", "all good")
111116
.build())
112117
.build();
113118
}
119+
120+
@GET
121+
public Response findAll() {
122+
return Response.accepted()
123+
.entity(eventStore.findAll())
124+
.build();
125+
}
126+
127+
@DELETE
128+
public Response cleanup() {
129+
eventStore.cleanupAll();
130+
return Response.accepted()
131+
.entity(Json.createObjectBuilder()
132+
.add("result", "event store cleared")
133+
.build())
134+
.build();
135+
}
114136
}

src/main/java/daggerok/eventstore/events/CounterCreated.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,8 @@ public CounterCreated(@JsonProperty("aggregateId") UUID aggregateId,
3434
this.counterName = Optional.ofNullable(counterName).orElse(String.format("counter-%d", System.nanoTime()));
3535
this.at = Optional.ofNullable(at).orElse(ZonedDateTime.now());
3636
}
37+
38+
public CounterCreated(UUID aggregateId) {
39+
this(aggregateId, null, null);
40+
}
3741
}

src/main/java/daggerok/eventstore/events/CounterIncremented.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,8 @@ public CounterIncremented(@JsonProperty("aggregateId") UUID aggregateId,
4040
this.withValue = Optional.ofNullable(withValue).orElse(1L);
4141
this.at = Optional.ofNullable(at).orElse(ZonedDateTime.now());
4242
}
43+
44+
public CounterIncremented(UUID aggregateId) {
45+
this(aggregateId, null, null, null);
46+
}
4347
}

0 commit comments

Comments
 (0)