Skip to content
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

add rollback mechanism. #17

Merged
merged 2 commits into from
Dec 18, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package edu.stanford.protege.webprotegeeventshistory;

import com.fasterxml.jackson.annotation.JsonProperty;
import edu.stanford.protege.webprotege.common.ChangeRequestId;
import edu.stanford.protege.webprotege.common.EventId;
import edu.stanford.protege.webprotege.common.ProjectEvent;
import edu.stanford.protege.webprotege.common.ProjectId;

import javax.annotation.Nonnull;

public record EntityUpdateFailedEvent(@JsonProperty("projectId") ProjectId projectId,
@JsonProperty("eventId") EventId eventId,

@JsonProperty("entityIri") String entityIri,
@JsonProperty("changeRequestId")ChangeRequestId changeRequestId) implements ProjectEvent {

public static String CHANNEL = "webprotege.api.EntityUpdateFailed";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[question]: this event from rabbit can be handled multiple times by multiple services? basically everyone who subscribed to the channel will receive the notification right?



@Nonnull
@Override
public ProjectId projectId() {
return projectId;
}

@Nonnull
@Override
public EventId eventId() {
return eventId;
}

@Override
public String getChannel() {
return CHANNEL;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package edu.stanford.protege.webprotegeeventshistory;


import edu.stanford.protege.webprotege.ipc.EventHandler;
import edu.stanford.protege.webprotegeeventshistory.uiHistoryConcern.repositories.RevisionsEventRepository;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;

@Component
public class EntityUpdateFailedEventHandler implements EventHandler<EntityUpdateFailedEvent> {

private final RevisionsEventRepository repository;

public EntityUpdateFailedEventHandler(RevisionsEventRepository repository) {
this.repository = repository;
}

@Nonnull
@Override
public String getChannelName() {
return EntityUpdateFailedEvent.CHANNEL;
}

@Nonnull
@Override
public String getHandlerName() {
return EntityUpdateFailedEventHandler.class.getName();
}

@Override
public Class<EntityUpdateFailedEvent> getEventClass() {
return EntityUpdateFailedEvent.class;
}
@Override
public void handleEvent(EntityUpdateFailedEvent event) {
repository.deleteByChangeRequestIdAndWhoficEntityIri(event.changeRequestId().id(), event.entityIri());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@
public record NewRevisionsEvent(
EventId eventId,
ProjectId projectId,
Set<ProjectChangeForEntity> changes
Set<ProjectChangeForEntity> changes,
ChangeRequestId changeRequestId
) implements ProjectEvent {
public final static String CHANNEL = "webprotege.events.projects.uiHistory.NewRevisionsEvent";

@JsonCreator
public static NewRevisionsEvent create(@JsonProperty("eventId") EventId eventId,
@JsonProperty("projectId") ProjectId projectId,
@JsonProperty("changes") Set<ProjectChangeForEntity> changes) {
return new NewRevisionsEvent(eventId, projectId, changes);
@JsonProperty("changes") Set<ProjectChangeForEntity> changes,
@JsonProperty("changeRequestId") ChangeRequestId changeRequestId) {
return new NewRevisionsEvent(eventId, projectId, changes, changeRequestId);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.stanford.protege.webprotegeeventshistory.uiHistoryConcern.events;

import com.google.common.base.Objects;
import edu.stanford.protege.webprotege.common.ChangeRequestId;
import edu.stanford.protege.webprotege.common.ProjectId;
import edu.stanford.protege.webprotegeeventshistory.uiHistoryConcern.dto.ChangeType;
import org.springframework.data.mongodb.core.index.*;
Expand All @@ -14,6 +15,8 @@ public record RevisionsEvent(
String whoficEntityIri,
ChangeType changeType,
@Indexed(name = "timestamp", direction = IndexDirection.DESCENDING) long timestamp,

String changeRequestId,
org.bson.Document projectChange
) {

Expand All @@ -27,8 +30,9 @@ public static RevisionsEvent create(ProjectId projectId,
String whoficEntityIri,
ChangeType changeType,
long timestamp,
org.bson.Document projectChange) {
return new RevisionsEvent(projectId.id(), whoficEntityIri, changeType, timestamp, projectChange);
org.bson.Document projectChange,
ChangeRequestId changeRequestId) {
return new RevisionsEvent(projectId.id(), whoficEntityIri, changeType, timestamp, changeRequestId != null ? changeRequestId.id() : null, projectChange);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,16 @@ public RevisionEventMapper(ObjectMapper objectMapper) {

public List<RevisionsEvent> mapNewRevisionsEventToRevisionsEvents(NewRevisionsEvent newRevisionsEvent) {

List<RevisionsEvent> revisionsEvents = newRevisionsEvent.changes().stream()
return newRevisionsEvent.changes().stream()
.flatMap(projectChangeForEntity -> {
String whoficIri = projectChangeForEntity.whoficEntityIri();
ChangeType changeType = projectChangeForEntity.changeType();
ProjectChange projectChange = projectChangeForEntity.projectChange();
long timestamp = projectChange.getTimestamp();
var projectChangeDocument = objectMapper.convertValue(projectChange, Document.class);

return Stream.of(RevisionsEvent.create(newRevisionsEvent.projectId(), whoficIri, changeType, timestamp, projectChangeDocument));
return Stream.of(RevisionsEvent.create(newRevisionsEvent.projectId(), whoficIri, changeType, timestamp, projectChangeDocument, newRevisionsEvent.changeRequestId()));
})
.toList();

return revisionsEvents;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import edu.stanford.protege.webprotege.common.ProjectId;
import edu.stanford.protege.webprotegeeventshistory.uiHistoryConcern.events.RevisionsEvent;
import org.springframework.data.mongodb.repository.*;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

Expand All @@ -11,5 +12,6 @@ public interface RevisionsEventRepository extends MongoRepository<RevisionsEvent
List<RevisionsEvent> findByProjectIdAndTimestampAfter(String projectId, long timestamp);

List<RevisionsEvent> findByProjectIdAndWhoficEntityIriOrderByTimestampDesc(ProjectId projectId, String whoficEntityIri);

@Transactional
void deleteByChangeRequestIdAndWhoficEntityIri(String changeRequestId, String whoficEntityIri);
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public Page<ProjectChange> fetchPaginatedProjectChanges(ProjectId projectId, Opt
entityIriSubject,
null,
0,
null,
null
);
ExampleMatcher matcher = ExampleMatcher.matching()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.stanford.protege.webprotegeeventshistory.uiHistoryConcern.handlers;

import edu.stanford.protege.webprotege.common.ChangeRequestId;
import edu.stanford.protege.webprotege.common.ProjectId;
import edu.stanford.protege.webprotegeeventshistory.*;
import edu.stanford.protege.webprotegeeventshistory.uiHistoryConcern.dto.*;
Expand Down Expand Up @@ -68,8 +69,8 @@ private void insertMockRevisionsEvent(ProjectId projectId, String entityIri, lon
entityIri,
changeType,
timestamp,
new Document()
);
new Document(),
ChangeRequestId.generate());
mongoTemplate.save(revisionsEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private OWLEntity mockOWLEntity(String iri) {
private ProjectChange insertMockRevisionsEvent(ProjectId projectId, String whoficEntityIri, long timestamp) {
ProjectChange projectChange = ProjectChange.get(RevisionNumber.getRevisionNumber(1), UserId.valueOf("user1"), timestamp, "Description1", 0, Page.emptyPage());
org.bson.Document projectChangeDocument = objectMapper.convertValue(projectChange, Document.class);
RevisionsEvent revisionsEvent = RevisionsEvent.create(projectId, whoficEntityIri, ChangeType.UPDATE_ENTITY, timestamp, projectChangeDocument);
RevisionsEvent revisionsEvent = RevisionsEvent.create(projectId, whoficEntityIri, ChangeType.UPDATE_ENTITY, timestamp, projectChangeDocument, ChangeRequestId.generate());
mongoTemplate.save(revisionsEvent);

return projectChange;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void GIVEN_validNewLinearizationRevisionsEvent_WHEN_handleEventCalled_THE
changes.add(ProjectChangeForEntity.create("whoficEntityIri1", projectChange1));
changes.add(ProjectChangeForEntity.create("whoficEntityIri2", projectChange2));

NewRevisionsEvent newLinRevEvent = NewRevisionsEvent.create(EventId.generate(), projectId, changes);
NewRevisionsEvent newLinRevEvent = NewRevisionsEvent.create(EventId.generate(), projectId, changes, ChangeRequestId.generate());

handler.handleEvent(newLinRevEvent);

Expand All @@ -73,7 +73,7 @@ public void GIVEN_emptyChanges_WHEN_handleEventCalled_THEN_noEventsAreSavedToDat
ProjectId projectId = ProjectId.generate();
Set<ProjectChangeForEntity> emptyChanges = new LinkedHashSet<>();

NewRevisionsEvent emptyEvent = NewRevisionsEvent.create(EventId.generate(), projectId, emptyChanges);
NewRevisionsEvent emptyEvent = NewRevisionsEvent.create(EventId.generate(), projectId, emptyChanges, ChangeRequestId.generate());

handler.handleEvent(emptyEvent);

Expand Down Expand Up @@ -106,8 +106,8 @@ public void GIVEN_multipleValidEvents_WHEN_handleEventCalled_THEN_allEventsAreRe

changesForSecondEvent.add(ProjectChangeForEntity.create("whoficEntityIri3", projectChange3));

NewRevisionsEvent firstEvent = NewRevisionsEvent.create(EventId.generate(), projectId1, changesForFirstEvent);
NewRevisionsEvent secondEvent = NewRevisionsEvent.create(EventId.generate(), projectId2, changesForSecondEvent);
NewRevisionsEvent firstEvent = NewRevisionsEvent.create(EventId.generate(), projectId1, changesForFirstEvent, ChangeRequestId.generate());
NewRevisionsEvent secondEvent = NewRevisionsEvent.create(EventId.generate(), projectId2, changesForSecondEvent, ChangeRequestId.generate());

handler.handleEvent(firstEvent);
handler.handleEvent(secondEvent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void GIVEN_validProjectIdAndChanges_WHEN_mapNewLinearizationRevisionsEven
Document mockDocument = new Document();
when(objectMapper.convertValue(mockProjectChange, Document.class)).thenReturn(mockDocument);

List<RevisionsEvent> result = revisionEventMapper.mapNewRevisionsEventToRevisionsEvents(NewRevisionsEvent.create(EventId.generate(), projectId, changes));
List<RevisionsEvent> result = revisionEventMapper.mapNewRevisionsEventToRevisionsEvents(NewRevisionsEvent.create(EventId.generate(), projectId, changes, ChangeRequestId.generate()));

assertNotNull(result);
assertEquals(2, result.size());
Expand All @@ -63,7 +63,7 @@ public void GIVEN_emptyChangesSet_WHEN_mapNewLinearizationRevisionsEventToRevisi
ProjectId projectId = new ProjectId("testProjectId");
Set<ProjectChangeForEntity> emptyChanges = Set.of();

List<RevisionsEvent> result = revisionEventMapper.mapNewRevisionsEventToRevisionsEvents(NewRevisionsEvent.create(EventId.generate(), projectId, emptyChanges));
List<RevisionsEvent> result = revisionEventMapper.mapNewRevisionsEventToRevisionsEvents(NewRevisionsEvent.create(EventId.generate(), projectId, emptyChanges, ChangeRequestId.generate()));

assertNotNull(result);
assertTrue(result.isEmpty());
Expand All @@ -75,7 +75,7 @@ public void GIVEN_nullChangesSet_WHEN_mapNewLinearizationRevisionsEventToRevisio
ProjectId projectId = new ProjectId("testProjectId");
Set<ProjectChangeForEntity> nullChanges = null;

assertThrows(NullPointerException.class, () -> revisionEventMapper.mapNewRevisionsEventToRevisionsEvents(NewRevisionsEvent.create(EventId.generate(), projectId, nullChanges)));
assertThrows(NullPointerException.class, () -> revisionEventMapper.mapNewRevisionsEventToRevisionsEvents(NewRevisionsEvent.create(EventId.generate(), projectId, nullChanges, ChangeRequestId.generate())));

verifyNoInteractions(objectMapper);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ public void setUp() {
@Test
public void GIVEN_validNewLinearizationRevisionsEvent_WHEN_registerEventCalled_THEN_revisionsEventsSavedToRepository() {
Set<ProjectChangeForEntity> changes = Set.of(mock(ProjectChangeForEntity.class));
NewRevisionsEvent event = NewRevisionsEvent.create(EventId.generate(), projectId, changes);
NewRevisionsEvent event = NewRevisionsEvent.create(EventId.generate(), projectId, changes, ChangeRequestId.generate());

RevisionsEvent mockRevisionsEvent = RevisionsEvent.create(projectId, "whoficEntityIri", ChangeType.UPDATE_ENTITY, 12345L, new Document());
RevisionsEvent mockRevisionsEvent = RevisionsEvent.create(projectId, "whoficEntityIri", ChangeType.UPDATE_ENTITY, 12345L, new Document(), ChangeRequestId.generate());
when(revisionEventMapper.mapNewRevisionsEventToRevisionsEvents(event))
.thenReturn(List.of(mockRevisionsEvent));

Expand All @@ -72,7 +72,7 @@ public void GIVEN_validProjectIdAndSubject_WHEN_fetchPaginatedProjectChangesCall
IRI mockIri = IRI.create("http://example.com/entity");
when(mockEntity.getIRI()).thenReturn(mockIri);

RevisionsEvent mockRevisionsEvent = RevisionsEvent.create(projectId, mockIri.toString(), ChangeType.UPDATE_ENTITY, 12345L, new Document());
RevisionsEvent mockRevisionsEvent = RevisionsEvent.create(projectId, mockIri.toString(), ChangeType.UPDATE_ENTITY, 12345L, new Document(), ChangeRequestId.generate());
PageRequest pageRequest = PageRequest.of(0, 1, Sort.by(Sort.Direction.DESC, "timestamp"));
org.springframework.data.domain.Page<RevisionsEvent> mockPage = new PageImpl<>(List.of(mockRevisionsEvent), pageRequest, 1);

Expand All @@ -94,7 +94,7 @@ public void GIVEN_validProjectIdAndSubject_WHEN_fetchPaginatedProjectChangesCall
@Test
public void GIVEN_nullSubject_WHEN_fetchPaginatedProjectChangesCalled_THEN_returnPaginatedProjectChanges() {

RevisionsEvent mockRevisionsEvent = RevisionsEvent.create(projectId, null, ChangeType.CREATE_ENTITY, 12345L, new Document());
RevisionsEvent mockRevisionsEvent = RevisionsEvent.create(projectId, null, ChangeType.CREATE_ENTITY, 12345L, new Document(), ChangeRequestId.generate());
PageRequest pageRequest = PageRequest.of(0, 1, Sort.by(Sort.Direction.DESC, "timestamp"));
org.springframework.data.domain.Page<RevisionsEvent> mockPage = new PageImpl<>(List.of(mockRevisionsEvent), pageRequest, 1);

Expand Down Expand Up @@ -145,9 +145,9 @@ public void GIVEN_noEntitiesChangedAfterTimestamp_WHEN_getChangedEntitiesAfterTi

@Test
public void GIVEN_entitiesChangedAfterTimestamp_WHEN_getChangedEntitiesAfterTimestampCalled_THEN_returnGroupedChangedEntities() {
RevisionsEvent createdEntity = RevisionsEvent.create(projectId, "entityIRI1", ChangeType.CREATE_ENTITY, timestamp.getTime(), new Document());
RevisionsEvent updatedEntity = RevisionsEvent.create(projectId, "entityIRI2", ChangeType.UPDATE_ENTITY, timestamp.getTime() + 1000, new Document());
RevisionsEvent deletedEntity = RevisionsEvent.create(projectId, "entityIRI3", ChangeType.DELETE_ENTITY, timestamp.getTime() + 2000, new Document());
RevisionsEvent createdEntity = RevisionsEvent.create(projectId, "entityIRI1", ChangeType.CREATE_ENTITY, timestamp.getTime(), new Document(), ChangeRequestId.generate());
RevisionsEvent updatedEntity = RevisionsEvent.create(projectId, "entityIRI2", ChangeType.UPDATE_ENTITY, timestamp.getTime() + 1000, new Document(), ChangeRequestId.generate());
RevisionsEvent deletedEntity = RevisionsEvent.create(projectId, "entityIRI3", ChangeType.DELETE_ENTITY, timestamp.getTime() + 2000, new Document(), ChangeRequestId.generate());

when(repository.findByProjectIdAndTimestampAfter(projectId.id(), timestamp.getTime())).thenReturn(List.of(createdEntity, updatedEntity, deletedEntity));

Expand All @@ -167,9 +167,9 @@ public void GIVEN_entitiesChangedAfterTimestamp_WHEN_getChangedEntitiesAfterTime

@Test
public void GIVEN_multipleEntitiesChangedAfterTimestamp_WHEN_getChangedEntitiesAfterTimestampCalled_THEN_returnDeduplicatedChangedEntities() {
RevisionsEvent createdEntity1 = RevisionsEvent.create(projectId, "entityIRI1", ChangeType.CREATE_ENTITY, timestamp.getTime(), new Document());
RevisionsEvent createdEntity2 = RevisionsEvent.create(projectId, "entityIRI1", ChangeType.CREATE_ENTITY, timestamp.getTime() + 1000, new Document());
RevisionsEvent updatedEntity = RevisionsEvent.create(projectId, "entityIRI2", ChangeType.UPDATE_ENTITY, timestamp.getTime() + 2000, new Document());
RevisionsEvent createdEntity1 = RevisionsEvent.create(projectId, "entityIRI1", ChangeType.CREATE_ENTITY, timestamp.getTime(), new Document(), ChangeRequestId.generate());
RevisionsEvent createdEntity2 = RevisionsEvent.create(projectId, "entityIRI1", ChangeType.CREATE_ENTITY, timestamp.getTime() + 1000, new Document(), ChangeRequestId.generate());
RevisionsEvent updatedEntity = RevisionsEvent.create(projectId, "entityIRI2", ChangeType.UPDATE_ENTITY, timestamp.getTime() + 2000, new Document(), ChangeRequestId.generate());

when(repository.findByProjectIdAndTimestampAfter(projectId.id(), timestamp.getTime())).thenReturn(List.of(createdEntity1, createdEntity2, updatedEntity));

Expand Down Expand Up @@ -212,8 +212,8 @@ public void GIVEN_validHistoryForEntity_WHEN_getEntityHistorySummaryCalled_THEN_
Document document1 = new Document("a", "b");
Document document2 = new Document("c", "d");

RevisionsEvent eventCreate = RevisionsEvent.create(projectId, entityIri, ChangeType.CREATE_ENTITY, timestamp2, document2);
RevisionsEvent eventUpdate = RevisionsEvent.create(projectId, entityIri, ChangeType.UPDATE_ENTITY, timestamp1, document1);
RevisionsEvent eventCreate = RevisionsEvent.create(projectId, entityIri, ChangeType.CREATE_ENTITY, timestamp2, document2, ChangeRequestId.generate());
RevisionsEvent eventUpdate = RevisionsEvent.create(projectId, entityIri, ChangeType.UPDATE_ENTITY, timestamp1, document1, ChangeRequestId.generate());

ProjectChange changeCreate = ProjectChange.get(
RevisionNumber.getRevisionNumber(1),
Expand Down
Loading