Skip to content
This repository has been archived by the owner on Aug 13, 2020. It is now read-only.

Create service for reporting event errors #795

Merged
merged 1 commit into from
Dec 12, 2019
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on [Keep a CHANGELOG](http://keepachangelog.com/). This project adheres to
## [Unreleased]
- Update framework-api to 4.2.0
- Add table in system database 'event_error_log' for storing errors with events that failed to process
- New SystemErrorService for reporting system errors

## [6.4.0] - 2019-11-12
### Changed
Expand Down
53 changes: 53 additions & 0 deletions framework-system/framework-system-errors/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>framework-system</artifactId>
<groupId>uk.gov.justice.services</groupId>
<version>6.5.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>framework-system-errors</artifactId>

<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>framework-system-persistence</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.justice.framework-api</groupId>
<artifactId>framework-api-system-errors</artifactId>
<version>${framework-api.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.justice.framework-api</groupId>
<artifactId>framework-api-core</artifactId>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>framework-utilities</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package uk.gov.justice.services.framework.system.errors;

import static java.lang.String.format;
import static javax.transaction.Transactional.TxType.NOT_SUPPORTED;

import uk.gov.justice.services.common.util.UtcClock;
import uk.gov.justice.services.framework.system.errors.SystemErrorService;
import uk.gov.justice.services.framework.utilities.exceptions.StackTraceProvider;
import uk.gov.justice.services.messaging.JsonEnvelope;
import uk.gov.justice.services.messaging.Metadata;
import uk.gov.justice.services.system.domain.EventError;
import uk.gov.justice.services.system.persistence.EventErrorLogRepository;

import java.time.Clock;
import java.util.Optional;
import java.util.UUID;

import javax.inject.Inject;
import javax.transaction.Transactional;

public class DefaultSystemErrorService implements SystemErrorService {

private static final Long MISSING_EVENT_NUMBER = -1L;
private static final String NO_COMMENT = "";

@Inject
private EventErrorLogRepository eventErrorLogRepository;

@Inject
private StackTraceProvider stackTraceProvider;

@Inject
private UtcClock clock;

@Override
@Transactional(NOT_SUPPORTED)
public void reportError(
final String messageId,
final String componentName,
final JsonEnvelope jsonEnvelope,
final Throwable exception) {

final Metadata metadata = jsonEnvelope.metadata();
final UUID eventId = metadata.id();
final Optional<Long> eventNumber = metadata.eventNumber();

final EventError eventError = new EventError(
eventId,
eventNumber.orElse(MISSING_EVENT_NUMBER),
componentName,
messageId,
metadata.asJsonObject().toString(),
jsonEnvelope.payload().toString(),
exception.getMessage(),
stackTraceProvider.getStackTrace(exception),
clock.now(),
getComment(eventNumber)
);

eventErrorLogRepository.save(eventError);
}

private String getComment(final Optional<Long> eventNumberOptional) {

if (eventNumberOptional.isPresent()) {
return NO_COMMENT;
}

return format("Event number is missing from event. Setting to %d instead", MISSING_EVENT_NUMBER);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package uk.gov.justice.services.framework.system.errors;

import static java.util.Optional.empty;
import static java.util.Optional.of;
import static java.util.UUID.randomUUID;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import uk.gov.justice.services.common.util.UtcClock;
import uk.gov.justice.services.framework.utilities.exceptions.StackTraceProvider;
import uk.gov.justice.services.messaging.JsonEnvelope;
import uk.gov.justice.services.messaging.Metadata;
import uk.gov.justice.services.system.domain.EventError;
import uk.gov.justice.services.system.persistence.EventErrorLogRepository;

import java.time.ZonedDateTime;
import java.util.UUID;

import javax.json.JsonObject;
import javax.json.JsonValue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class DefaultSystemErrorServiceTest {

private static final String AN_EMPTY_STRING = "";

@Mock
private EventErrorLogRepository eventErrorLogRepository;

@Mock
private StackTraceProvider stackTraceProvider;

@Mock
private UtcClock clock;

@InjectMocks
private DefaultSystemErrorService defaultSystemErrorService;

@Captor
private ArgumentCaptor<EventError> eventErrorCaptor;

@Test
public void shouldCreateEventErrorAndPersist() throws Exception {

final String messageId = "message id";
final String componentName = "EVENT_LISTENER";
final String errorMessage = "Help help we're all going to die";
final Throwable exception = new NullPointerException(errorMessage);
final Long eventNumber = 239874L;
final UUID eventId = randomUUID();
final String metadataJson = "{metadata: stuff}";
final String payloadJson = "{payload: stuff}";
final String stackTrace = "the stacktrace";
final ZonedDateTime erroredAt = new UtcClock().now();

final JsonEnvelope jsonEnvelope = mock(JsonEnvelope.class);
final Metadata metadata = mock(Metadata.class);
final JsonObject metadataJsonObject = mock(JsonObject.class);
final JsonValue payload = mock(JsonValue.class);

when(jsonEnvelope.metadata()).thenReturn(metadata);
when(metadata.id()).thenReturn(eventId);
when(metadata.eventNumber()).thenReturn(of(eventNumber));

when(metadata.asJsonObject()).thenReturn(metadataJsonObject);
when(jsonEnvelope.payload()).thenReturn(payload);

when(metadataJsonObject.toString()).thenReturn(metadataJson);
when(payload.toString()).thenReturn(payloadJson);
when(stackTraceProvider.getStackTrace(exception)).thenReturn(stackTrace);

when(clock.now()).thenReturn(erroredAt);

defaultSystemErrorService.reportError(
messageId,
componentName,
jsonEnvelope,
exception
);

verify(eventErrorLogRepository).save(eventErrorCaptor.capture());

final EventError eventError = eventErrorCaptor.getValue();

assertThat(eventError.getMessageId(), is(messageId));
assertThat(eventError.getComponent(), is(componentName));
assertThat(eventError.getErrorMessage(), is(errorMessage));
assertThat(eventError.getEventNumber(), is(eventNumber));
assertThat(eventError.getEventId(), is(eventId));
assertThat(eventError.getMetadata(), is(metadataJson));
assertThat(eventError.getPayload(), is(payloadJson));
assertThat(eventError.getStacktrace(), is(stackTrace));
assertThat(eventError.getErroredAt(), is(erroredAt));
assertThat(eventError.getComments(), is(AN_EMPTY_STRING));
}

@Test
public void shouldHandleMissingEventNumber() throws Exception {

final String messageId = "message id";
final String componentName = "EVENT_LISTENER";
final String errorMessage = "Help help we're all going to die";
final Throwable exception = new NullPointerException(errorMessage);
final UUID eventId = randomUUID();
final String metadataJson = "{metadata: stuff}";
final String payloadJson = "{payload: stuff}";
final String stackTrace = "the stacktrace";
final ZonedDateTime erroredAt = new UtcClock().now();

final JsonEnvelope jsonEnvelope = mock(JsonEnvelope.class);
final Metadata metadata = mock(Metadata.class);
final JsonObject metadataJsonObject = mock(JsonObject.class);
final JsonValue payload = mock(JsonValue.class);

when(jsonEnvelope.metadata()).thenReturn(metadata);
when(metadata.id()).thenReturn(eventId);
when(metadata.eventNumber()).thenReturn(empty());

when(metadata.asJsonObject()).thenReturn(metadataJsonObject);
when(jsonEnvelope.payload()).thenReturn(payload);

when(metadataJsonObject.toString()).thenReturn(metadataJson);
when(payload.toString()).thenReturn(payloadJson);
when(stackTraceProvider.getStackTrace(exception)).thenReturn(stackTrace);

when(clock.now()).thenReturn(erroredAt);

defaultSystemErrorService.reportError(
messageId,
componentName,
jsonEnvelope,
exception
);

verify(eventErrorLogRepository).save(eventErrorCaptor.capture());

final EventError eventError = eventErrorCaptor.getValue();

assertThat(eventError.getMessageId(), is(messageId));
assertThat(eventError.getComponent(), is(componentName));
assertThat(eventError.getErrorMessage(), is(errorMessage));
assertThat(eventError.getEventNumber(), is(-1L));
assertThat(eventError.getEventId(), is(eventId));
assertThat(eventError.getMetadata(), is(metadataJson));
assertThat(eventError.getPayload(), is(payloadJson));
assertThat(eventError.getStacktrace(), is(stackTrace));
assertThat(eventError.getErroredAt(), is(erroredAt));
assertThat(eventError.getComments(), is("Event number is missing from event. Setting to -1 instead"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.3.xsd">

<changeSet
id="framework-system-005"
author="TechPod"
logicalFilePath="005-add-comment-column-to-event-error-log-table.xml">

<addColumn tableName="event_error_log">
<column name="comments" type="TEXT">
<constraints nullable="true"/>
</column>
</addColumn>

<rollback>
<dropColumn tableName="event_error_log" columnName="comments"/>
</rollback>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class EventError {
private final String errorMessage;
private final String stacktrace;
private final ZonedDateTime erroredAt;
private final String comments;

public EventError(
final UUID eventId,
Expand All @@ -25,7 +26,8 @@ public EventError(
final String payload,
final String errorMessage,
final String stacktrace,
final ZonedDateTime erroredAt) {
final ZonedDateTime erroredAt,
final String comments) {
this.eventId = eventId;
this.eventNumber = eventNumber;
this.component = component;
Expand All @@ -35,6 +37,7 @@ public EventError(
this.errorMessage = errorMessage;
this.stacktrace = stacktrace;
this.erroredAt = erroredAt;
this.comments = comments;
}

public UUID getEventId() {
Expand Down Expand Up @@ -73,6 +76,10 @@ public ZonedDateTime getErroredAt() {
return erroredAt;
}

public String getComments() {
return comments;
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
Expand All @@ -86,12 +93,13 @@ public boolean equals(final Object o) {
Objects.equals(payload, that.payload) &&
Objects.equals(errorMessage, that.errorMessage) &&
Objects.equals(stacktrace, that.stacktrace) &&
Objects.equals(erroredAt, that.erroredAt);
Objects.equals(erroredAt, that.erroredAt) &&
Objects.equals(comments, that.comments);
}

@Override
public int hashCode() {
return Objects.hash(eventId, eventNumber, component, messageId, metadata, payload, errorMessage, stacktrace, erroredAt);
return Objects.hash(eventId, eventNumber, component, messageId, metadata, payload, errorMessage, stacktrace, erroredAt, comments);
}

@Override
Expand All @@ -106,6 +114,7 @@ public String toString() {
", errorMessage='" + errorMessage + '\'' +
", stacktrace='" + stacktrace + '\'' +
", erroredAt=" + erroredAt +
", comments='" + comments + '\'' +
'}';
}
}
Loading