From ffb9495e151df3fa4e2a42486b598b66b72192f2 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 20 Sep 2023 16:53:16 +0200 Subject: [PATCH] GH-301 - Polishing. Formatting, visibility. Some refactorings in Neo4jEventPublicationRepository. --- .../spring-modulith-events-neo4j/pom.xml | 92 +++--- .../events/neo4j/Neo4jEventPublication.java | 24 +- ...eo4jEventPublicationAutoConfiguration.java | 9 +- .../Neo4jEventPublicationRepository.java | 287 ++++++++++-------- .../events/neo4j/Neo4jIndexInitializer.java | 31 +- .../modulith/events/neo4j/package-info.java | 5 + .../Neo4jEventPublicationRepositoryTest.java | 99 +++--- .../neo4j/Neo4jIndexInitializerTest.java | 60 ++-- .../modulith/testapp/TestApplication.java | 18 +- .../spring-modulith-example-epr-neo4j/pom.xml | 84 ++--- .../example/ApplicationIntegrationTests.java | 11 +- .../spring-modulith-starter-neo4j/pom.xml | 87 +++--- 12 files changed, 471 insertions(+), 336 deletions(-) create mode 100644 spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/package-info.java diff --git a/spring-modulith-events/spring-modulith-events-neo4j/pom.xml b/spring-modulith-events/spring-modulith-events-neo4j/pom.xml index e3beba04..c7cc7e65 100644 --- a/spring-modulith-events/spring-modulith-events-neo4j/pom.xml +++ b/spring-modulith-events/spring-modulith-events-neo4j/pom.xml @@ -1,58 +1,58 @@ - - 4.0.0 + + 4.0.0 - - org.springframework.modulith - spring-modulith-events - 1.1.0-SNAPSHOT - ../pom.xml - + + org.springframework.modulith + spring-modulith-events + 1.1.0-SNAPSHOT + ../pom.xml + - Spring Modulith - Events - Neo4j-based repository - spring-modulith-events-neo4j + Spring Modulith - Events - Neo4j-based repository + spring-modulith-events-neo4j - - org.springframework.modulith.events.neo4j - + + org.springframework.modulith.events.neo4j + - + - - ${project.groupId} - spring-modulith-events-core - ${project.version} - + + ${project.groupId} + spring-modulith-events-core + ${project.version} + - - org.springframework.data - spring-data-neo4j - + + org.springframework.data + spring-data-neo4j + - - org.springframework.boot - spring-boot-starter-test - test - + + org.springframework.boot + spring-boot-starter-test + test + - - org.springframework.boot - spring-boot-starter-data-neo4j - test - + + org.springframework.boot + spring-boot-starter-data-neo4j + test + - - org.testcontainers - neo4j - 1.19.0 - test - + + org.testcontainers + neo4j + test + - - org.testcontainers - junit-jupiter - 1.19.0 - test - + + org.testcontainers + junit-jupiter + test + - + diff --git a/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublication.java b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublication.java index 745325ae..5d7d3d7d 100644 --- a/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublication.java +++ b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublication.java @@ -1,15 +1,30 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.modulith.events.neo4j; import java.time.Instant; import java.util.UUID; /** - * * The event publication entity definition. * * @author Gerrit Meier + * @since 1.1 */ -public class Neo4jEventPublication { +class Neo4jEventPublication { public final UUID identifier; public final Instant publicationDate; @@ -19,12 +34,13 @@ public class Neo4jEventPublication { public Instant completionDate; - public Neo4jEventPublication(UUID identifier, Instant publicationDate, String listenerId, Object event, String eventHash) { + public Neo4jEventPublication(UUID identifier, Instant publicationDate, String listenerId, Object event, + String eventHash) { + this.identifier = identifier; this.publicationDate = publicationDate; this.listenerId = listenerId; this.event = event; this.eventHash = eventHash; } - } diff --git a/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationAutoConfiguration.java b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationAutoConfiguration.java index f29739d2..2702983d 100644 --- a/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationAutoConfiguration.java +++ b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationAutoConfiguration.java @@ -12,14 +12,19 @@ import org.springframework.modulith.events.config.EventPublicationConfigurationE import org.springframework.modulith.events.core.EventSerializer; /** + * Auto-configuration to register a {@link Neo4jEventPublicationRepository}, a default {@link Configuration} and a + * {@link Neo4jIndexInitializer} if enabled. + * * @author Gerrit Meier + * @since 1.1 */ @AutoConfiguration @AutoConfigureBefore(EventPublicationAutoConfiguration.class) -public class Neo4jEventPublicationAutoConfiguration implements EventPublicationConfigurationExtension { +class Neo4jEventPublicationAutoConfiguration implements EventPublicationConfigurationExtension { @Bean - Neo4jEventPublicationRepository neo4jEventPublicationRepository(Neo4jClient neo4jClient, Configuration cypherDslConfiguration, EventSerializer eventSerializer) { + Neo4jEventPublicationRepository neo4jEventPublicationRepository(Neo4jClient neo4jClient, + Configuration cypherDslConfiguration, EventSerializer eventSerializer) { return new Neo4jEventPublicationRepository(neo4jClient, cypherDslConfiguration, eventSerializer); } diff --git a/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationRepository.java b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationRepository.java index 787059eb..5849cb4c 100644 --- a/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationRepository.java +++ b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationRepository.java @@ -1,21 +1,20 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.modulith.events.neo4j; -import org.neo4j.cypherdsl.core.Cypher; -import org.neo4j.cypherdsl.core.Node; -import org.neo4j.cypherdsl.core.Statement; -import org.neo4j.cypherdsl.core.renderer.Configuration; -import org.neo4j.cypherdsl.core.renderer.Renderer; -import org.neo4j.driver.Values; -import org.neo4j.driver.types.TypeSystem; -import org.springframework.data.neo4j.core.Neo4jClient; -import org.springframework.modulith.events.core.EventPublicationRepository; -import org.springframework.modulith.events.core.EventSerializer; -import org.springframework.modulith.events.core.PublicationTargetIdentifier; -import org.springframework.modulith.events.core.TargetEventPublication; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.Assert; -import org.springframework.util.DigestUtils; - import java.time.Instant; import java.time.ZoneOffset; import java.util.List; @@ -24,6 +23,30 @@ import java.util.Objects; import java.util.Optional; import java.util.UUID; +import org.neo4j.cypherdsl.core.Cypher; +import org.neo4j.cypherdsl.core.Node; +import org.neo4j.cypherdsl.core.ResultStatement; +import org.neo4j.cypherdsl.core.Statement; +import org.neo4j.cypherdsl.core.renderer.Configuration; +import org.neo4j.cypherdsl.core.renderer.Renderer; +import org.neo4j.driver.Values; +import org.neo4j.driver.types.TypeSystem; +import org.springframework.data.neo4j.core.Neo4jClient; +import org.springframework.lang.Nullable; +import org.springframework.modulith.events.core.EventPublicationRepository; +import org.springframework.modulith.events.core.EventSerializer; +import org.springframework.modulith.events.core.PublicationTargetIdentifier; +import org.springframework.modulith.events.core.TargetEventPublication; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.DigestUtils; + +/** + * A {@link Neo4jClient} based implementation of {@link EventPublicationRepository}. + * + * @author Gerrit Meier + * @since 1.1 + */ @Transactional class Neo4jEventPublicationRepository implements EventPublicationRepository { @@ -35,20 +58,75 @@ class Neo4jEventPublicationRepository implements EventPublicationRepository { private static final String PUBLICATION_DATE = "publicationDate"; private static final String COMPLETION_DATE = "completionDate"; - private static final Node eventPublicationNode = Cypher.node("Neo4jEventPublication").named("neo4jEventPublication"); + private static final Node EVENT_PUBLICATION_NODE = Cypher.node("Neo4jEventPublication") + .named("neo4jEventPublication"); + + private static final Statement INCOMPLETE_BY_EVENT_AND_TARGET_IDENTIFIER_STATEMENT = Cypher + .match(EVENT_PUBLICATION_NODE) + .where(EVENT_PUBLICATION_NODE.property(EVENT_HASH).eq(Cypher.parameter(EVENT_HASH))) + .and(EVENT_PUBLICATION_NODE.property(LISTENER_ID).eq(Cypher.parameter(LISTENER_ID))) + .and(EVENT_PUBLICATION_NODE.property(COMPLETION_DATE).isNull()) + .returning(EVENT_PUBLICATION_NODE) + .build(); + + private static final Statement DELETE_BY_ID_STATEMENT = Cypher.match(EVENT_PUBLICATION_NODE) + .where(EVENT_PUBLICATION_NODE.property(ID).in(Cypher.parameter(ID))) + .delete(EVENT_PUBLICATION_NODE) + .build(); + + private static final Statement DELETE_COMPLETED_STATEMENT = Cypher.match(EVENT_PUBLICATION_NODE) + .where(EVENT_PUBLICATION_NODE.property(COMPLETION_DATE).isNotNull()) + .delete(EVENT_PUBLICATION_NODE) + .build(); + + private static final Statement DELETE_COMPLETED_BEFORE_STATEMENT = Cypher.match(EVENT_PUBLICATION_NODE) + .where(EVENT_PUBLICATION_NODE.property(PUBLICATION_DATE).lt(Cypher.parameter(PUBLICATION_DATE))) + .and(EVENT_PUBLICATION_NODE.property(COMPLETION_DATE).isNotNull()) + .delete(EVENT_PUBLICATION_NODE) + .build(); + + private static final Statement INCOMPLETE_PUBLISHED_BEFORE_STATEMENT = Cypher + .match(EVENT_PUBLICATION_NODE) + .where(EVENT_PUBLICATION_NODE.property(PUBLICATION_DATE).lt(Cypher.parameter(PUBLICATION_DATE))) + .and(EVENT_PUBLICATION_NODE.property(COMPLETION_DATE).isNull()) + .returning(EVENT_PUBLICATION_NODE) + .orderBy(EVENT_PUBLICATION_NODE.property(PUBLICATION_DATE)) + .build(); + + private static final Statement CREATE_STATEMENT = Cypher.create(EVENT_PUBLICATION_NODE) + .set(EVENT_PUBLICATION_NODE.property(ID).to(Cypher.parameter(ID))) + .set(EVENT_PUBLICATION_NODE.property(EVENT_SERIALIZED).to(Cypher.parameter(EVENT_SERIALIZED))) + .set(EVENT_PUBLICATION_NODE.property(EVENT_HASH).to(Cypher.parameter(EVENT_HASH))) + .set(EVENT_PUBLICATION_NODE.property(EVENT_TYPE).to(Cypher.parameter(EVENT_TYPE))) + .set(EVENT_PUBLICATION_NODE.property(LISTENER_ID).to(Cypher.parameter(LISTENER_ID))) + .set(EVENT_PUBLICATION_NODE.property(PUBLICATION_DATE).to(Cypher.parameter(PUBLICATION_DATE))) + .build(); + + private static final Statement COMPLETE_STATEMENT = Cypher.match(EVENT_PUBLICATION_NODE) + .where(EVENT_PUBLICATION_NODE.property(EVENT_HASH).eq(Cypher.parameter(EVENT_HASH))) + .and(EVENT_PUBLICATION_NODE.property(LISTENER_ID).eq(Cypher.parameter(LISTENER_ID))) + .set(EVENT_PUBLICATION_NODE.property(COMPLETION_DATE).to(Cypher.parameter(COMPLETION_DATE))) + .build(); + + private static final ResultStatement INCOMPLETE_STATEMENT = Cypher.match(EVENT_PUBLICATION_NODE) + .where(EVENT_PUBLICATION_NODE.property(COMPLETION_DATE).isNull()) + .returning(EVENT_PUBLICATION_NODE) + .orderBy(EVENT_PUBLICATION_NODE.property(PUBLICATION_DATE)) + .build(); private final Neo4jClient neo4jClient; - private final Configuration cypherDslConfiguration; + private final Renderer renderer; private final EventSerializer eventSerializer; - Neo4jEventPublicationRepository(Neo4jClient neo4jClient, Configuration cypherDslConfiguration, EventSerializer eventSerializer) { + Neo4jEventPublicationRepository(Neo4jClient neo4jClient, Configuration cypherDslConfiguration, + EventSerializer eventSerializer) { Assert.notNull(neo4jClient, "Neo4jClient must not be null!"); Assert.notNull(cypherDslConfiguration, "CypherDSL configuration must not be null!"); Assert.notNull(eventSerializer, "EventSerializer must not be null!"); this.neo4jClient = neo4jClient; - this.cypherDslConfiguration = cypherDslConfiguration; + this.renderer = Renderer.getRenderer(cypherDslConfiguration); this.eventSerializer = eventSerializer; } @@ -66,28 +144,18 @@ class Neo4jEventPublicationRepository implements EventPublicationRepository { var event = publication.getEvent(); var eventType = event.getClass().getName(); - var eventSerialized = (String) eventSerializer.serialize(event); + var eventSerialized = eventSerializer.serialize(event).toString(); var eventHash = DigestUtils.md5DigestAsHex(eventSerialized.getBytes()); - var createStatement = Cypher.create(eventPublicationNode) - .set(eventPublicationNode.property(ID).to(Cypher.parameter(ID))) - .set(eventPublicationNode.property(EVENT_SERIALIZED).to(Cypher.parameter(EVENT_SERIALIZED))) - .set(eventPublicationNode.property(EVENT_HASH).to(Cypher.parameter(EVENT_HASH))) - .set(eventPublicationNode.property(EVENT_TYPE).to(Cypher.parameter(EVENT_TYPE))) - .set(eventPublicationNode.property(LISTENER_ID).to(Cypher.parameter(LISTENER_ID))) - .set(eventPublicationNode.property(PUBLICATION_DATE).to(Cypher.parameter(PUBLICATION_DATE))) - .build(); - - neo4jClient.query(renderStatement(createStatement)) - .bindAll(Map.of( - ID, Values.value(identifier.toString()), - EVENT_SERIALIZED, eventSerialized, - EVENT_HASH, eventHash, - EVENT_TYPE, eventType, - LISTENER_ID, listenerId, - PUBLICATION_DATE, Values.value(publicationDate.atOffset(ZoneOffset.UTC)) - )) - .run(); + neo4jClient.query(renderer.render(CREATE_STATEMENT)) + .bindAll(Map.of( + ID, Values.value(identifier.toString()), + EVENT_SERIALIZED, eventSerialized, + EVENT_HASH, eventHash, + EVENT_TYPE, eventType, + LISTENER_ID, listenerId, + PUBLICATION_DATE, Values.value(publicationDate.atOffset(ZoneOffset.UTC)))) + .run(); return publication; } @@ -99,19 +167,14 @@ class Neo4jEventPublicationRepository implements EventPublicationRepository { @Override @Transactional public void markCompleted(Object event, PublicationTargetIdentifier identifier, Instant completionDate) { - var eventHash = DigestUtils.md5DigestAsHex(((String) eventSerializer.serialize(event)).getBytes()); - var completeStatement = Cypher.match(eventPublicationNode) - .where(eventPublicationNode.property(EVENT_HASH).eq(Cypher.parameter(EVENT_HASH))) - .and(eventPublicationNode.property(LISTENER_ID).eq(Cypher.parameter(LISTENER_ID))) - .set(eventPublicationNode.property(COMPLETION_DATE).to(Cypher.parameter(COMPLETION_DATE))) - .build(); + var eventHash = DigestUtils.md5DigestAsHex(eventSerializer.serialize(event).toString().getBytes()); - neo4jClient.query(renderStatement(completeStatement)) - .bind(eventHash).to(EVENT_HASH) - .bind(identifier.getValue()).to(LISTENER_ID) - .bind(Values.value(completionDate.atOffset(ZoneOffset.UTC))).to(COMPLETION_DATE) - .run(); + neo4jClient.query(renderer.render(COMPLETE_STATEMENT)) + .bind(eventHash).to(EVENT_HASH) + .bind(identifier.getValue()).to(LISTENER_ID) + .bind(Values.value(completionDate.atOffset(ZoneOffset.UTC))).to(COMPLETION_DATE) + .run(); } /* @@ -122,99 +185,85 @@ class Neo4jEventPublicationRepository implements EventPublicationRepository { @Transactional(readOnly = true) public List findIncompletePublications() { - var findIncompleteStatement = Cypher.match(eventPublicationNode) - .where(eventPublicationNode.property(COMPLETION_DATE).isNull()) - .returning(eventPublicationNode) - .orderBy(eventPublicationNode.property(PUBLICATION_DATE)) - .build(); - - return List.copyOf(neo4jClient.query(renderStatement(findIncompleteStatement)) - .fetchAs(TargetEventPublication.class) - .mappedBy(this::mapRecordToPublication) - .all()); - + return List.copyOf(neo4jClient.query(renderer.render(INCOMPLETE_STATEMENT)) + .fetchAs(TargetEventPublication.class) + .mappedBy(this::mapRecordToPublication) + .all()); } + /* + * (non-Javadoc) + * @see org.springframework.modulith.events.core.EventPublicationRepository#findIncompletePublicationsPublishedBefore(java.time.Instant) + */ @Override @Transactional(readOnly = true) public List findIncompletePublicationsPublishedBefore(Instant instant) { - var findIncompleteStatement = Cypher.match(eventPublicationNode) - .where(eventPublicationNode.property(PUBLICATION_DATE).lt(Cypher.parameter(PUBLICATION_DATE))) - .and(eventPublicationNode.property(COMPLETION_DATE).isNull()) - .returning(eventPublicationNode) - .orderBy(eventPublicationNode.property(PUBLICATION_DATE)) - .build(); - - return List.copyOf(neo4jClient.query(renderStatement(findIncompleteStatement)) - .bind(Values.value(instant.atOffset(ZoneOffset.UTC))).to(PUBLICATION_DATE) - .fetchAs(TargetEventPublication.class) - .mappedBy(this::mapRecordToPublication) - .all()); + return List.copyOf(neo4jClient.query(renderer.render(INCOMPLETE_PUBLISHED_BEFORE_STATEMENT)) + .bind(Values.value(instant.atOffset(ZoneOffset.UTC))).to(PUBLICATION_DATE) + .fetchAs(TargetEventPublication.class) + .mappedBy(this::mapRecordToPublication) + .all()); } + /* + * (non-Javadoc) + * @see org.springframework.modulith.events.core.EventPublicationRepository#findIncompletePublicationsByEventAndTargetIdentifier(java.lang.Object, org.springframework.modulith.events.core.PublicationTargetIdentifier) + */ @Override @Transactional(readOnly = true) - public Optional findIncompletePublicationsByEventAndTargetIdentifier(Object event, PublicationTargetIdentifier targetIdentifier) { + public Optional findIncompletePublicationsByEventAndTargetIdentifier(Object event, + PublicationTargetIdentifier targetIdentifier) { var eventHash = DigestUtils.md5DigestAsHex(((String) eventSerializer.serialize(event)).getBytes()); var listenerId = targetIdentifier.getValue(); - var statement = Cypher.match(eventPublicationNode) - .where(eventPublicationNode.property(EVENT_HASH).eq(Cypher.parameter(EVENT_HASH))) - .and(eventPublicationNode.property(LISTENER_ID).eq(Cypher.parameter(LISTENER_ID))) - .and(eventPublicationNode.property(COMPLETION_DATE).isNull()) - .returning(eventPublicationNode) - .build(); - - return neo4jClient.query(renderStatement(statement)) - .bindAll(Map.of(EVENT_HASH, eventHash, LISTENER_ID, listenerId)) - .fetchAs(TargetEventPublication.class) - .mappedBy(this::mapRecordToPublication) - .one(); + return neo4jClient.query(renderer.render(INCOMPLETE_BY_EVENT_AND_TARGET_IDENTIFIER_STATEMENT)) + .bindAll(Map.of(EVENT_HASH, eventHash, LISTENER_ID, listenerId)) + .fetchAs(TargetEventPublication.class) + .mappedBy(this::mapRecordToPublication) + .one(); } + /* + * (non-Javadoc) + * @see org.springframework.modulith.events.core.EventPublicationRepository#deletePublications(java.util.List) + */ @Override @Transactional public void deletePublications(List identifiers) { - var deleteStatement = Cypher.match(eventPublicationNode) - .where(eventPublicationNode.property(ID).in(Cypher.parameter(ID))) - .delete(eventPublicationNode) - .build(); - neo4jClient.query(renderStatement(deleteStatement)) - .bind(identifiers.stream().map(UUID::toString).toList()).to(ID) - .run(); + neo4jClient.query(renderer.render(DELETE_BY_ID_STATEMENT)) + .bind(identifiers.stream().map(UUID::toString).toList()).to(ID) + .run(); } + /* + * (non-Javadoc) + * @see org.springframework.modulith.events.core.EventPublicationRepository#deleteCompletedPublications() + */ @Override @Transactional public void deleteCompletedPublications() { - var deleteStatement = Cypher.match(eventPublicationNode) - .where(eventPublicationNode.property(COMPLETION_DATE).isNotNull()) - .delete(eventPublicationNode) - .build(); - - neo4jClient.query(renderStatement(deleteStatement)) - .run(); + neo4jClient.query(renderer.render(DELETE_COMPLETED_STATEMENT)).run(); } + /* + * (non-Javadoc) + * @see org.springframework.modulith.events.core.EventPublicationRepository#deleteCompletedPublicationsBefore(java.time.Instant) + */ @Override @Transactional public void deleteCompletedPublicationsBefore(Instant instant) { - var deleteStatement = Cypher.match(eventPublicationNode) - .where(eventPublicationNode.property(PUBLICATION_DATE).lt(Cypher.parameter(PUBLICATION_DATE))) - .and(eventPublicationNode.property(COMPLETION_DATE).isNotNull()) - .delete(eventPublicationNode) - .build(); - neo4jClient.query(renderStatement(deleteStatement)) - .bind(Values.value(instant.atOffset(ZoneOffset.UTC))).to(PUBLICATION_DATE) - .run(); + neo4jClient.query(renderer.render(DELETE_COMPLETED_BEFORE_STATEMENT)) + .bind(Values.value(instant.atOffset(ZoneOffset.UTC))).to(PUBLICATION_DATE) + .run(); } private Neo4jEventPublicationAdapter mapRecordToPublication(TypeSystem typeSystem, org.neo4j.driver.Record record) { - var publicationNode = record.get(eventPublicationNode.getRequiredSymbolicName().getValue()).asNode(); + + var publicationNode = record.get(EVENT_PUBLICATION_NODE.getRequiredSymbolicName().getValue()).asNode(); var identifier = UUID.fromString(publicationNode.get(ID).asString()); var publicationDate = publicationNode.get(PUBLICATION_DATE).asZonedDateTime().toInstant(); var listenerId = publicationNode.get(LISTENER_ID).asString(); @@ -223,21 +272,19 @@ class Neo4jEventPublicationRepository implements EventPublicationRepository { var eventType = publicationNode.get(EVENT_TYPE).asString(); try { - Object event = eventSerializer.deserialize(eventSerialized, Class.forName(eventType)); - Neo4jEventPublication publication = new Neo4jEventPublication(identifier, publicationDate, listenerId, event, eventHash); + + var event = eventSerializer.deserialize(eventSerialized, Class.forName(eventType)); + var publication = new Neo4jEventPublication(identifier, publicationDate, listenerId, event, + eventHash); return new Neo4jEventPublicationAdapter(publication); + } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } - private String renderStatement(Statement statement) { - return Renderer.getRenderer(cypherDslConfiguration).render(statement); - } - - - public static class Neo4jEventPublicationAdapter implements TargetEventPublication { + private static class Neo4jEventPublicationAdapter implements TargetEventPublication { private final Neo4jEventPublication delegate; @@ -285,7 +332,7 @@ class Neo4jEventPublicationRepository implements EventPublicationRepository { * @see java.lang.Object#equals(java.lang.Object) */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; @@ -307,4 +354,4 @@ class Neo4jEventPublicationRepository implements EventPublicationRepository { return Objects.hash(delegate); } } -} \ No newline at end of file +} diff --git a/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jIndexInitializer.java b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jIndexInitializer.java index 6ac78da1..31b901ab 100644 --- a/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jIndexInitializer.java +++ b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/Neo4jIndexInitializer.java @@ -1,23 +1,46 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.modulith.events.neo4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.data.neo4j.core.Neo4jClient; /** + * Automatically creates an index on the {@link Neo4jEventPublication#eventHash} field. + * * @author Gerrit Meier + * @since 1.1 */ -public class Neo4jIndexInitializer implements InitializingBean { +class Neo4jIndexInitializer implements InitializingBean { private final Neo4jClient neo4jClient; - public Neo4jIndexInitializer(Neo4jClient neo4jClient) { + Neo4jIndexInitializer(Neo4jClient neo4jClient) { this.neo4jClient = neo4jClient; } + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ @Override public void afterPropertiesSet() throws Exception { + neo4jClient - .query("CREATE INDEX eventHashIndex IF NOT EXISTS FOR (n:`Neo4jEventPublication`) ON (n.eventHash)") - .run(); + .query("CREATE INDEX eventHashIndex IF NOT EXISTS FOR (n:`Neo4jEventPublication`) ON (n.eventHash)") + .run(); } } diff --git a/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/package-info.java b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/package-info.java new file mode 100644 index 00000000..f4fec4fc --- /dev/null +++ b/spring-modulith-events/spring-modulith-events-neo4j/src/main/java/org/springframework/modulith/events/neo4j/package-info.java @@ -0,0 +1,5 @@ +/** + * Neo4j integration for {@link org.springframework.modulith.events.core.EventPublicationRepository}. + */ +@org.springframework.lang.NonNullApi +package org.springframework.modulith.events.neo4j; diff --git a/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationRepositoryTest.java b/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationRepositoryTest.java index eb05ba37..b767ff6d 100644 --- a/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationRepositoryTest.java +++ b/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/events/neo4j/Neo4jEventPublicationRepositoryTest.java @@ -1,13 +1,21 @@ package org.springframework.modulith.events.neo4j; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + import lombok.Value; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.UUID; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.neo4j.cypherdsl.core.renderer.Dialect; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.types.Node; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; @@ -24,14 +32,6 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.when; - /** * @author Gerrit Meier */ @@ -39,23 +39,19 @@ import static org.mockito.Mockito.when; @Testcontainers(disabledWithoutDocker = true) class Neo4jEventPublicationRepositoryTest { - @Container - private static Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:5")) - .withRandomPassword(); + @Container // + static final Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:5")) + .withRandomPassword(); static final PublicationTargetIdentifier TARGET_IDENTIFIER = PublicationTargetIdentifier.of("listener"); - @Autowired - private Neo4jEventPublicationRepository repository; - - @Autowired - private Driver driver; - - @MockBean - private EventSerializer eventSerializer; + @Autowired Neo4jEventPublicationRepository repository; + @Autowired Driver driver; + @MockBean EventSerializer eventSerializer; @BeforeEach void clearDb() { + try (var session = driver.session()) { session.run("MATCH (n) detach delete n").consume(); } @@ -63,6 +59,7 @@ class Neo4jEventPublicationRepositoryTest { @Test void createEventPublication() { + var testEvent = new TestEvent("id"); var eventSerialized = "{\"eventId\":\"id\"}"; var eventHash = DigestUtils.md5DigestAsHex(eventSerialized.getBytes()); @@ -71,22 +68,27 @@ class Neo4jEventPublicationRepositoryTest { var publication = repository.create(TargetEventPublication.of(testEvent, TARGET_IDENTIFIER)); try (var session = driver.session()) { - var result = session.run("MATCH (p:Neo4jEventPublication) return p") - .single(); - Node neo4jEventPublicationNode = result.get("p").asNode(); - assertThat(UUID.fromString(neo4jEventPublicationNode.get("identifier").asString())).isEqualTo(publication.getIdentifier()); - assertThat(neo4jEventPublicationNode.get("publicationDate").asZonedDateTime().toInstant()).isEqualTo(publication.getPublicationDate()); - assertThat(neo4jEventPublicationNode.get("listenerId").asString()).isEqualTo(publication.getTargetIdentifier().getValue()); + var result = session.run("MATCH (p:Neo4jEventPublication) return p") + .single(); + + var neo4jEventPublicationNode = result.get("p").asNode(); + + assertThat(UUID.fromString(neo4jEventPublicationNode.get("identifier").asString())) + .isEqualTo(publication.getIdentifier()); + assertThat(neo4jEventPublicationNode.get("publicationDate").asZonedDateTime().toInstant()) + .isEqualTo(publication.getPublicationDate()); + assertThat(neo4jEventPublicationNode.get("listenerId").asString()) + .isEqualTo(publication.getTargetIdentifier().getValue()); assertThat(neo4jEventPublicationNode.get("completionDate").isNull()).isTrue(); assertThat(neo4jEventPublicationNode.get("eventSerialized").asString()).isEqualTo(eventSerialized); assertThat(neo4jEventPublicationNode.get("eventHash").asString()).isEqualTo(eventHash); } - } @Test void updateEventPublication() { + var testEvent1 = new TestEvent("id1"); var event1Serialized = "{\"eventId\":\"id1\"}"; var testEvent2 = new TestEvent("id2"); @@ -103,12 +105,13 @@ class Neo4jEventPublicationRepositoryTest { repository.markCompleted(event1, now); assertThat(repository.findIncompletePublications()).hasSize(1) - .element(0) - .extracting(TargetEventPublication::getEvent).isEqualTo(event2.getEvent()); + .element(0) + .extracting(TargetEventPublication::getEvent).isEqualTo(event2.getEvent()); } @Test void findInCompletePastPublications() { + var testEvent = new TestEvent("id"); var eventSerialized = "{\"eventId\":\"id\"}"; @@ -121,14 +124,15 @@ class Neo4jEventPublicationRepositoryTest { var older = Instant.now().minus(1L, ChronoUnit.MINUTES); assertThat(repository.findIncompletePublicationsPublishedBefore(newer)).hasSize(1) - .element(0) - .extracting(TargetEventPublication::getEvent).isEqualTo(event.getEvent()); + .element(0) + .extracting(TargetEventPublication::getEvent).isEqualTo(event.getEvent()); assertThat(repository.findIncompletePublicationsPublishedBefore(older)).hasSize(0); } @Test void findIncompleteByEventAndTargetIdentifier() { + var testEvent = new TestEvent("id"); var eventSerialized = "{\"eventId\":\"id\"}"; @@ -138,13 +142,15 @@ class Neo4jEventPublicationRepositoryTest { var event = repository.create(TargetEventPublication.of(testEvent, TARGET_IDENTIFIER)); assertThat(repository.findIncompletePublicationsByEventAndTargetIdentifier(testEvent, event.getTargetIdentifier())) - .isPresent(); + .isPresent(); } @Test void deletePublicationById() { - TestEvent testEvent = new TestEvent("id"); + + var testEvent = new TestEvent("id"); var eventSerialized = "{\"eventId\":\"id\"}"; + when(eventSerializer.serialize(testEvent)).thenReturn(eventSerialized); var event = repository.create(TargetEventPublication.of(testEvent, TARGET_IDENTIFIER)); @@ -156,9 +162,10 @@ class Neo4jEventPublicationRepositoryTest { @Test void deleteCompletedPublications() { - TestEvent testEvent1 = new TestEvent("id1"); + + var testEvent1 = new TestEvent("id1"); var event1Serialized = "{\"eventId\":\"id1\"}"; - TestEvent testEvent2 = new TestEvent("id2"); + var testEvent2 = new TestEvent("id2"); var event2Serialized = "{\"eventId\":\"id2\"}"; when(eventSerializer.serialize(testEvent1)).thenReturn(event1Serialized); @@ -173,16 +180,18 @@ class Neo4jEventPublicationRepositoryTest { repository.deleteCompletedPublications(); try (var session = driver.session()) { - var count = session.run("MATCH (n) WHERE n.completionDate is not null return count(n)").single().get("count(n)").asLong(); + var count = session.run("MATCH (n) WHERE n.completionDate is not null return count(n)").single().get("count(n)") + .asLong(); assertThat(count).isEqualTo(0); } } @Test void deleteCompletedPublicationsBefore() throws Exception { - TestEvent testEvent1 = new TestEvent("id1"); + + var testEvent1 = new TestEvent("id1"); var event1Serialized = "{\"eventId\":\"id1\"}"; - TestEvent testEvent2 = new TestEvent("id2"); + var testEvent2 = new TestEvent("id2"); var event2Serialized = "{\"eventId\":\"id2\"}"; when(eventSerializer.serialize(testEvent1)).thenReturn(event1Serialized); @@ -203,27 +212,31 @@ class Neo4jEventPublicationRepositoryTest { assertThat(repository.findIncompletePublications()).hasSize(0); try (var session = driver.session()) { + var records = session.run("MATCH (n) WHERE n.completionDate is not null return n").list(); + assertThat(records.size()).isEqualTo(1); assertThat(records.get(0).get("n").asNode().get("eventSerialized").asString()).contains("id2"); } } @Value - private static final class TestEvent { + static class TestEvent { String eventId; } - @Import({TestApplication.class}) + @Import({ TestApplication.class }) @Configuration static class Config { + @Bean - public Driver driver() { - return GraphDatabase.driver(neo4jContainer.getBoltUrl(), AuthTokens.basic("neo4j", neo4jContainer.getAdminPassword())); + Driver driver() { + return GraphDatabase.driver(neo4jContainer.getBoltUrl(), + AuthTokens.basic("neo4j", neo4jContainer.getAdminPassword())); } @Bean - public org.neo4j.cypherdsl.core.renderer.Configuration cypherDslConfiguration() { + org.neo4j.cypherdsl.core.renderer.Configuration cypherDslConfiguration() { return org.neo4j.cypherdsl.core.renderer.Configuration.newConfig().withDialect(Dialect.NEO4J_5).build(); } } diff --git a/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/events/neo4j/Neo4jIndexInitializerTest.java b/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/events/neo4j/Neo4jIndexInitializerTest.java index 44b4b89c..b9ce6041 100644 --- a/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/events/neo4j/Neo4jIndexInitializerTest.java +++ b/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/events/neo4j/Neo4jIndexInitializerTest.java @@ -1,5 +1,24 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.modulith.events.neo4j; +import static org.assertj.core.api.Assertions.*; + +import java.util.Optional; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.neo4j.driver.AuthTokens; @@ -20,34 +39,28 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; -import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; - /** * @author Gerrit Meier */ -public class Neo4jIndexInitializerTest { +class Neo4jIndexInitializerTest { - @ImportAutoConfiguration({Neo4jEventPublicationAutoConfiguration.class, TestBase.Config.class}) + @ImportAutoConfiguration({ Neo4jEventPublicationAutoConfiguration.class, TestBase.Config.class }) @Testcontainers(disabledWithoutDocker = true) @ContextConfiguration(classes = TestApplication.class) static class TestBase { - @Container - private static Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:5")) - .withRandomPassword(); - - @MockBean - EventSerializer eventSerializer; + @Container private static Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:5")) + .withRandomPassword(); + @MockBean EventSerializer eventSerializer; @Configuration static class Config { @Bean Driver driver() { - return GraphDatabase.driver(neo4jContainer.getBoltUrl(), AuthTokens.basic("neo4j", neo4jContainer.getAdminPassword())); + return GraphDatabase.driver(neo4jContainer.getBoltUrl(), + AuthTokens.basic("neo4j", neo4jContainer.getAdminPassword())); } } } @@ -55,11 +68,9 @@ public class Neo4jIndexInitializerTest { @Nested @DataNeo4jTest(properties = "spring.modulith.events.neo4j.event-index.enabled=true") class WithIndexEnabled extends TestBase { - @Autowired - Neo4jClient neo4jClient; - @Autowired - Optional neo4jIndexInitializer; + @Autowired Neo4jClient neo4jClient; + @Autowired Optional neo4jIndexInitializer; @Test void indexInitializerBeanIsPresent() { @@ -69,19 +80,17 @@ public class Neo4jIndexInitializerTest { @Test void indexWasCreated() { assertThat(neo4jClient.query("SHOW INDEX YIELD name") - .fetchAs(String.class) - .all()).contains("eventHashIndex"); + .fetchAs(String.class) + .all()).contains("eventHashIndex"); } } @Nested @DataNeo4jTest(properties = "spring.modulith.events.neo4j.event-index.enabled=false") class WithoutIndexEnabled extends TestBase { - @Autowired - Neo4jClient neo4jClient; - @Autowired - Optional neo4jIndexInitializer; + @Autowired Neo4jClient neo4jClient; + @Autowired Optional neo4jIndexInitializer; @Test void indexInitializerBeanIsNotPresent() { @@ -91,9 +100,8 @@ public class Neo4jIndexInitializerTest { @Test void indexWasNotCreated() { assertThat(neo4jClient.query("SHOW INDEX YIELD name") - .fetchAs(String.class) - .all()).doesNotContain("eventHashIndex"); + .fetchAs(String.class) + .all()).doesNotContain("eventHashIndex"); } } - } diff --git a/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/testapp/TestApplication.java b/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/testapp/TestApplication.java index ba5fbad8..5a628a0e 100644 --- a/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/testapp/TestApplication.java +++ b/spring-modulith-events/spring-modulith-events-neo4j/src/test/java/org/springframework/modulith/testapp/TestApplication.java @@ -1,3 +1,18 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.modulith.testapp; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -6,5 +21,4 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; * @author Gerrit Meier */ @SpringBootApplication -public class TestApplication { -} +public class TestApplication {} diff --git a/spring-modulith-examples/spring-modulith-example-epr-neo4j/pom.xml b/spring-modulith-examples/spring-modulith-example-epr-neo4j/pom.xml index ef8f3d68..3699d02d 100644 --- a/spring-modulith-examples/spring-modulith-example-epr-neo4j/pom.xml +++ b/spring-modulith-examples/spring-modulith-example-epr-neo4j/pom.xml @@ -1,56 +1,58 @@ - + - 4.0.0 + 4.0.0 - - org.springframework.modulith - spring-modulith-examples - 1.1.0-SNAPSHOT - ../pom.xml - + + org.springframework.modulith + spring-modulith-examples + 1.1.0-SNAPSHOT + ../pom.xml + - spring-modulith-example-epr-neo4j - Spring Modulith - Examples - EPR Neo4j Example + spring-modulith-example-epr-neo4j + Spring Modulith - Examples - EPR Neo4j Example - + - - org.springframework.modulith - spring-modulith-starter-neo4j - + + org.springframework.modulith + spring-modulith-starter-neo4j + - + - - org.jmolecules - jmolecules-events - + + org.jmolecules + jmolecules-events + - + - - org.springframework.boot - spring-boot-starter-data-neo4j - + + org.springframework.boot + spring-boot-starter-data-neo4j + - - org.testcontainers - junit-jupiter - test - + + org.testcontainers + junit-jupiter + test + - - org.testcontainers - neo4j - test - + + org.testcontainers + neo4j + test + - - org.springframework.boot - spring-boot-testcontainers - test - + + org.springframework.boot + spring-boot-testcontainers + test + - + diff --git a/spring-modulith-examples/spring-modulith-example-epr-neo4j/src/test/java/example/ApplicationIntegrationTests.java b/spring-modulith-examples/spring-modulith-example-epr-neo4j/src/test/java/example/ApplicationIntegrationTests.java index d87d775b..dd1500a7 100644 --- a/spring-modulith-examples/spring-modulith-example-epr-neo4j/src/test/java/example/ApplicationIntegrationTests.java +++ b/spring-modulith-examples/spring-modulith-example-epr-neo4j/src/test/java/example/ApplicationIntegrationTests.java @@ -17,6 +17,9 @@ package example; import example.inventory.InventoryUpdated; import example.order.OrderManagement; + +import java.util.Collection; + import org.junit.jupiter.api.Test; import org.neo4j.cypherdsl.core.renderer.Configuration; import org.neo4j.cypherdsl.core.renderer.Dialect; @@ -34,8 +37,6 @@ import org.springframework.modulith.test.Scenario; import org.testcontainers.containers.Neo4jContainer; import org.testcontainers.junit.jupiter.Testcontainers; -import java.util.Collection; - /** * @author Oliver Drotbohm * @author Gerrit Meier @@ -46,7 +47,7 @@ import java.util.Collection; class ApplicationIntegrationTests { @TestConfiguration - static class MongoDbInfrastructureConfiguration { + static class InfrastructureConfiguration { @Bean @ServiceConnection @@ -55,12 +56,12 @@ class ApplicationIntegrationTests { } @Bean - public Driver driver(Neo4jContainer container) { + Driver driver(Neo4jContainer container) { return GraphDatabase.driver(container.getBoltUrl(), AuthTokens.basic("neo4j", container.getAdminPassword())); } @Bean - public Configuration cypherDslConfiguration() { + Configuration cypherDslConfiguration() { return Configuration.newConfig().withDialect(Dialect.NEO4J_5).build(); } } diff --git a/spring-modulith-starters/spring-modulith-starter-neo4j/pom.xml b/spring-modulith-starters/spring-modulith-starter-neo4j/pom.xml index a51f17b1..ec016cc9 100644 --- a/spring-modulith-starters/spring-modulith-starter-neo4j/pom.xml +++ b/spring-modulith-starters/spring-modulith-starter-neo4j/pom.xml @@ -1,54 +1,55 @@ - 4.0.0 + 4.0.0 - - org.springframework.modulith - spring-modulith-starters - 1.1.0-SNAPSHOT - ../pom.xml - + + org.springframework.modulith + spring-modulith-starters + 1.1.0-SNAPSHOT + ../pom.xml + - spring-modulith-starter-neo4j - Spring Modulith - Starters - Starter Neo4j + spring-modulith-starter-neo4j + Spring Modulith - Starters - Starter Neo4j - - org.springframework.modulith.starter.neo4j - + + org.springframework.modulith.starter.neo4j + - + - - org.springframework.modulith - spring-modulith-starter-core - 1.1.0-SNAPSHOT - + + org.springframework.modulith + spring-modulith-starter-core + 1.1.0-SNAPSHOT + - - - org.springframework.modulith - spring-modulith-events-api - 1.1.0-SNAPSHOT - - - org.springframework.modulith - spring-modulith-events-core - 1.1.0-SNAPSHOT - runtime - - - org.springframework.modulith - spring-modulith-events-jackson - 1.1.0-SNAPSHOT - runtime - - - org.springframework.modulith - spring-modulith-events-neo4j - 1.1.0-SNAPSHOT - runtime - + - + + org.springframework.modulith + spring-modulith-events-api + 1.1.0-SNAPSHOT + + + org.springframework.modulith + spring-modulith-events-core + 1.1.0-SNAPSHOT + runtime + + + org.springframework.modulith + spring-modulith-events-jackson + 1.1.0-SNAPSHOT + runtime + + + org.springframework.modulith + spring-modulith-events-neo4j + 1.1.0-SNAPSHOT + runtime + + +