-
Notifications
You must be signed in to change notification settings - Fork 1
Adding mongodb realization for undo #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
rootProject.name = "spring-undo" | ||
include(":spring-undo-core") | ||
include(":spring-undo-redis") | ||
include(":spring-undo-redis") | ||
include(":spring-undo-mongo") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import Java_conventions_gradle.Versions | ||
|
||
plugins { | ||
id("java-conventions") | ||
id("testing-conventions") | ||
id("publishing-conventions") | ||
} | ||
|
||
description = "Spring-Undo persistence module. Stores records in mongodb and provides access to them." | ||
|
||
dependencies { | ||
compileOnly("org.springframework.boot:spring-boot-starter-data-mongodb:${Versions.springBootVersion}") | ||
compileOnly(project(":spring-undo-core")) | ||
|
||
testImplementation("org.springframework.boot:spring-boot-starter-data-mongodb:${Versions.springBootVersion}") | ||
testImplementation("org.testcontainers:testcontainers:1.17.1") | ||
testImplementation(project(":spring-undo-core")) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package dev.fomenko.springundomongo; | ||
|
||
import dev.fomenko.springundocore.dto.ActionRecord; | ||
|
||
public final class MongoConverter { | ||
|
||
private MongoConverter() { | ||
} | ||
|
||
public static RecordEntity<?> toEntity(ActionRecord<?> dto) { | ||
return RecordEntity.builder() | ||
.action(dto.getAction()) | ||
.expiresAt(dto.getExpiresAt()) | ||
.recordId(dto.getRecordId()) | ||
.build(); | ||
} | ||
|
||
|
||
public static ActionRecord<?> toDto(RecordEntity<?> dto) { | ||
return ActionRecord.builder() | ||
.action(dto.getAction()) | ||
.expiresAt(dto.getExpiresAt()) | ||
.recordId(dto.getRecordId()) | ||
.build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package dev.fomenko.springundomongo; | ||
|
||
|
||
import com.mongodb.client.result.DeleteResult; | ||
import dev.fomenko.springundocore.dto.ActionRecord; | ||
import dev.fomenko.springundocore.service.EventRecorder; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.apachecommons.CommonsLog; | ||
import org.springframework.data.mongodb.core.MongoTemplate; | ||
import org.springframework.data.mongodb.core.query.Criteria; | ||
import org.springframework.data.mongodb.core.query.Query; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
|
||
|
||
@CommonsLog | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SameNameButDifferent: The name Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? |
||
@RequiredArgsConstructor | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SameNameButDifferent: The name Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? |
||
public class MongoEventRecorder implements EventRecorder { | ||
public static final String COLLECTION_NAME = "recoverableEvents"; | ||
|
||
private final MongoTemplate mongoTemplate; | ||
|
||
@Override | ||
public void saveRecord(ActionRecord<?> actionRecord) { | ||
RecordEntity<?> recordEntity = MongoConverter.toEntity(actionRecord); | ||
mongoTemplate.save(recordEntity, COLLECTION_NAME); | ||
} | ||
|
||
@Override | ||
public List<ActionRecord<?>> getAllRecords() { | ||
List<RecordEntity> results = mongoTemplate.findAll(RecordEntity.class, COLLECTION_NAME); | ||
return results.stream() | ||
.map(MongoConverter::toDto) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
@Override | ||
public Optional<ActionRecord<?>> getRecordById(String recordId) { | ||
Query query = new Query(Criteria.where("recordId").is(recordId)); | ||
RecordEntity<?> recordEntity = mongoTemplate.findOne(query, RecordEntity.class, COLLECTION_NAME); | ||
return Optional.of(MongoConverter.toDto(recordEntity)); | ||
} | ||
|
||
@Override | ||
public boolean deleteRecordById(String recordId) { | ||
Query query = new Query(Criteria.where("recordId").is(recordId)); | ||
DeleteResult deleteResult = mongoTemplate.remove(query, RecordEntity.class, COLLECTION_NAME); | ||
long deletedCount = deleteResult.getDeletedCount(); | ||
return deletedCount > 0; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package dev.fomenko.springundomongo; | ||
|
||
import dev.fomenko.springundocore.service.EventRecorder; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.mongodb.core.MongoTemplate; | ||
|
||
@Configuration | ||
public class MongoUndoRecorderConfiguration { | ||
|
||
@Bean | ||
public EventRecorder mongoEventRecorder(MongoTemplate mongoTemplate) { | ||
return new MongoEventRecorder(mongoTemplate); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,22 @@ | ||||||||||
package dev.fomenko.springundomongo; | ||||||||||
|
||||||||||
import lombok.AllArgsConstructor; | ||||||||||
import lombok.Builder; | ||||||||||
import lombok.Data; | ||||||||||
import lombok.NoArgsConstructor; | ||||||||||
import org.springframework.data.annotation.Id; | ||||||||||
import org.springframework.data.mongodb.core.mapping.Document; | ||||||||||
|
||||||||||
import java.time.LocalDateTime; | ||||||||||
|
||||||||||
@Data | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SameNameButDifferent: The name Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? |
||||||||||
@Builder | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SameNameButDifferent: The name
Suggested change
Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MissingSummary: A summary fragment is required; consider using the value of the @return block as a summary fragment instead.
Suggested change
Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? |
||||||||||
@AllArgsConstructor | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SameNameButDifferent: The name Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? |
||||||||||
@NoArgsConstructor | ||||||||||
@Document | ||||||||||
public class RecordEntity<T> { | ||||||||||
@Id | ||||||||||
private String recordId; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SameNameButDifferent: The name Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? |
||||||||||
private T action; | ||||||||||
private LocalDateTime expiresAt; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SameNameButDifferent: The name Reply with "@sonatype-lift help" for info about LiftBot commands. When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands. Was this a good recommendation? |
||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=dev.fomenko.springundomongo.MongoUndoRecorderConfiguration |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
package dev.fomenko.springundomongo; | ||
|
||
import dev.fomenko.springundocore.dto.ActionRecord; | ||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.data.mongodb.core.MongoTemplate; | ||
|
||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import static dev.fomenko.springundomongo.MongoEventRecorder.COLLECTION_NAME; | ||
|
||
@SpringBootTest | ||
class MongoEventRecorderTest { | ||
@Autowired | ||
private MongoEventRecorder mongoEventRecorder; | ||
|
||
@Autowired | ||
private MongoTemplate mongoTemplate; | ||
|
||
@AfterEach | ||
void setup() { | ||
mongoTemplate.dropCollection(COLLECTION_NAME); | ||
} | ||
|
||
|
||
@Test | ||
void shouldSaveRecordCorrectly() { | ||
//given | ||
String recordId = "record_name"; | ||
LocalDateTime expiresAt = LocalDateTime.now(); | ||
String actionName = "action1"; | ||
String description = "desc"; | ||
int size = 382; | ||
|
||
ActionRecord<TestDto> actionTest = ActionRecord.<TestDto>builder() | ||
.recordId(recordId) | ||
.action(TestDto.builder() | ||
.testName(actionName) | ||
.testDescription(description) | ||
.size(size).build()) | ||
.expiresAt(expiresAt).build(); | ||
|
||
//when | ||
mongoEventRecorder.saveRecord(actionTest); | ||
|
||
//then | ||
List<RecordEntity> recordEntities = mongoTemplate.findAll(RecordEntity.class, COLLECTION_NAME); | ||
RecordEntity<TestDto> record = recordEntities.get(0); | ||
|
||
Assertions.assertEquals(1, recordEntities.size()); | ||
Assertions.assertEquals(actionName, record.getAction().getTestName()); | ||
Assertions.assertEquals(description, record.getAction().getTestDescription()); | ||
Assertions.assertEquals(size, record.getAction().getSize()); | ||
Assertions.assertEquals(recordId, record.getRecordId()); | ||
} | ||
|
||
|
||
@Test | ||
void shouldGetAllRecords() { | ||
//given | ||
LocalDateTime expiresAt1 = LocalDateTime.now(); | ||
String recordId1 = "event1"; | ||
String testName1 = "name1"; | ||
ActionRecord<TestDto> actionTest1 = ActionRecord.<TestDto>builder() | ||
.recordId(recordId1) | ||
.action(TestDto.builder() | ||
.testName(testName1) | ||
.testDescription("desc") | ||
.size(382).build()) | ||
.expiresAt(expiresAt1).build(); | ||
|
||
|
||
LocalDateTime expiresAt2 = LocalDateTime.now(); | ||
String recordId2 = "event2"; | ||
String testName2 = "name2"; | ||
ActionRecord<TestDto> actionTest2 = ActionRecord.<TestDto>builder() | ||
.recordId(recordId2) | ||
.action(TestDto.builder() | ||
.testName(testName2) | ||
.testDescription("desc") | ||
.size(382).build()) | ||
.expiresAt(expiresAt2).build(); | ||
|
||
//when | ||
mongoTemplate.save(MongoConverter.toEntity(actionTest1), COLLECTION_NAME); | ||
mongoTemplate.save(MongoConverter.toEntity(actionTest2), COLLECTION_NAME); | ||
|
||
//then | ||
List<ActionRecord<?>> allRecords = mongoEventRecorder.getAllRecords(); | ||
Assertions.assertEquals(2, allRecords.size()); | ||
|
||
Assertions.assertEquals(recordId1, allRecords.get(0).getRecordId()); | ||
Assertions.assertEquals(testName1, ((TestDto) allRecords.get(0).getAction()).getTestName()); | ||
|
||
Assertions.assertEquals(recordId2, allRecords.get(1).getRecordId()); | ||
Assertions.assertEquals(testName2, ((TestDto) allRecords.get(1).getAction()).getTestName()); | ||
} | ||
|
||
|
||
@Test | ||
void shouldGetRecordById() { | ||
//given | ||
String description = "description01"; | ||
String recordId = "record01"; | ||
int size = 2; | ||
String testName = "test01"; | ||
|
||
ActionRecord<TestDto> actionTest1 = ActionRecord.<TestDto>builder() | ||
.recordId(recordId) | ||
.action(TestDto.builder() | ||
.testName(testName) | ||
.testDescription(description) | ||
.size(size) | ||
.build()) | ||
.build(); | ||
|
||
ActionRecord<TestDto> actionTest2 = ActionRecord.<TestDto>builder() | ||
.recordId("record02") | ||
.action(TestDto.builder() | ||
.testDescription("description02") | ||
.size(12) | ||
.build()) | ||
.build(); | ||
|
||
mongoTemplate.save(MongoConverter.toEntity(actionTest1), COLLECTION_NAME); | ||
mongoTemplate.save(MongoConverter.toEntity(actionTest2), COLLECTION_NAME); | ||
|
||
//when | ||
Optional<ActionRecord<?>> recordById = mongoEventRecorder.getRecordById(recordId); | ||
|
||
//then | ||
ActionRecord<TestDto> resultRecord = (ActionRecord<TestDto>) recordById.get(); | ||
|
||
Assertions.assertEquals(recordId, resultRecord.getRecordId()); | ||
Assertions.assertEquals(testName, resultRecord.getAction().getTestName()); | ||
Assertions.assertEquals(description, resultRecord.getAction().getTestDescription()); | ||
Assertions.assertEquals(size, resultRecord.getAction().getSize()); | ||
} | ||
|
||
|
||
@Test | ||
void shouldDeleteRecordById() { | ||
//given | ||
String recordId1 = "event1"; | ||
ActionRecord<TestDto> actionTest1 = ActionRecord.<TestDto>builder() | ||
.recordId(recordId1) | ||
.action(TestDto.builder().build()) | ||
.expiresAt(LocalDateTime.now()).build(); | ||
|
||
String recordId2 = "event2"; | ||
ActionRecord<TestDto> actionTest2 = ActionRecord.<TestDto>builder() | ||
.recordId(recordId2) | ||
.action(TestDto.builder().build()) | ||
.expiresAt(LocalDateTime.now()).build(); | ||
|
||
mongoTemplate.save(MongoConverter.toEntity(actionTest1), COLLECTION_NAME); | ||
mongoTemplate.save(MongoConverter.toEntity(actionTest2), COLLECTION_NAME); | ||
|
||
//when | ||
boolean wasDeleted = mongoEventRecorder.deleteRecordById(recordId2); | ||
|
||
|
||
//then | ||
List<RecordEntity> results = mongoTemplate.findAll(RecordEntity.class, COLLECTION_NAME); | ||
|
||
Assertions.assertTrue(wasDeleted); | ||
Assertions.assertEquals(1, results.size()); | ||
Assertions.assertEquals(recordId1, results.get(0).getRecordId()); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package dev.fomenko.springundomongo; | ||
|
||
import com.mongodb.ConnectionString; | ||
import com.mongodb.MongoClientSettings; | ||
import com.mongodb.client.MongoClient; | ||
import com.mongodb.client.MongoClients; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; | ||
import org.testcontainers.containers.GenericContainer; | ||
|
||
@SpringBootApplication | ||
public class MongoEventRecorderTestConfiguration extends AbstractMongoClientConfiguration { | ||
private static final int MONGO_PORT = 27017; | ||
|
||
private final static GenericContainer<?> mongo = new GenericContainer<>("mongo:5.0.8") | ||
.withExposedPorts(MONGO_PORT); | ||
|
||
static { | ||
mongo.start(); | ||
} | ||
|
||
@Override | ||
protected String getDatabaseName() { | ||
return "test"; | ||
} | ||
|
||
|
||
@Override | ||
public MongoClient mongoClient() { | ||
String url = String.format("mongodb://%s:%s", mongo.getHost(), mongo.getMappedPort(MONGO_PORT)); | ||
return MongoClients.create(MongoClientSettings.builder() | ||
.applyConnectionString(new ConnectionString(url)) | ||
.build()); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package dev.fomenko.springundomongo; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
|
||
import java.io.Serializable; | ||
|
||
@Data | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@Builder | ||
public class TestDto implements Serializable { | ||
private String testName; | ||
private String testDescription; | ||
private Integer size; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical OSS Vulnerability:
pkg:maven/org.springframework.boot/[email protected]
1 Critical, 1 Severe, 0 Moderate, 0 Unknown vulnerabilities have been found across 2 dependencies
Components
pkg:maven/org.springframework/[email protected]
SEVERE Vulnerabilities (1)
pkg:maven/org.springframework.data/[email protected]
CRITICAL Vulnerabilities (1)
Reply with "@sonatype-lift help" for info about LiftBot commands.
Reply with "@sonatype-lift ignore" to tell LiftBot to leave out the above finding from this PR.
Reply with "@sonatype-lift ignoreall" to tell LiftBot to leave out all the findings from this PR and from the status bar in Github.
When talking to LiftBot, you need to refresh the page to see its response. Click here to get to know more about LiftBot commands.
Was this a good recommendation?
[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]