Skip to content

Commit

Permalink
Merge pull request #17 from protegeproject/remove-events-in-case-of-a…
Browse files Browse the repository at this point in the history
…pi-fail

add rollback mechanism.
  • Loading branch information
alexsilaghi authored Dec 18, 2024
2 parents e6a2d68 + 1ee9a15 commit 54c5dec
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 32 deletions.
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";


@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

0 comments on commit 54c5dec

Please sign in to comment.