Skip to content

Commit

Permalink
Alerter (#119)
Browse files Browse the repository at this point in the history
* added alerter events

* removed maintainers from repo

* small fixes
  • Loading branch information
ndc-dxc authored Jun 27, 2024
1 parent 645300b commit 37810c2
Show file tree
Hide file tree
Showing 48 changed files with 798 additions and 38 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-mail'

implementation 'org.mapstruct:mapstruct:1.5.5.Final'

Expand Down Expand Up @@ -203,6 +204,8 @@ jacocoTestCoverageVerification {
'it.gov.innovazione.ndc.harvester.service.ConfigService.NdcConfiguration',
'it.gov.innovazione.ndc.harvester.service.ConfigService',
'it.gov.innovazione.ndc.service.GithubService*',
'it.gov.innovazione.ndc.service.EmailService',
'it.gov.innovazione.ndc.service.AlerterMailSender',
'it.gov.innovazione.ndc.harvester.harvesters.utils.PathUtils',
'it.gov.innovazione.ndc.harvester.service.OnceLogger',
'it.gov.innovazione.ndc.harvester.service.ConfigReaderService',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package it.gov.innovazione.ndc.harvester.csv;

import it.gov.innovazione.ndc.eventhandler.NdcEventPublisher;
import it.gov.innovazione.ndc.repository.MarkerElasticSearchRepository;
import it.gov.innovazione.ndc.repository.VirtuosoClient;
import it.gov.innovazione.ndc.service.GithubService;
Expand Down Expand Up @@ -28,6 +29,9 @@ class NoDeepestLevelExtractorIntTest {
@MockBean
private GithubService githubService;

@MockBean
private NdcEventPublisher eventPublisher;

@Test
void shouldNotBeActivated() {
assertThat(extractors.stream().noneMatch(e -> e.getClass().isAssignableFrom(DeepestLevelExtractor.class))).isTrue();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package it.gov.innovazione.ndc.harvester.csv;

import it.gov.innovazione.ndc.eventhandler.NdcEventPublisher;
import it.gov.innovazione.ndc.repository.MarkerElasticSearchRepository;
import it.gov.innovazione.ndc.repository.VirtuosoClient;
import it.gov.innovazione.ndc.service.GithubService;
Expand Down Expand Up @@ -28,6 +29,9 @@ class UseDeepestLevelExtractorIntTest {
@MockBean
private GithubService githubService;

@MockBean
private NdcEventPublisher eventPublisher;

@Test
void shouldBeActivated() {
assertThat(extractors.stream().anyMatch(e -> e.getClass().isAssignableFrom(DeepestLevelExtractor.class))).isTrue();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package it.gov.innovazione.ndc.integration;

import io.restassured.response.Response;
import it.gov.innovazione.ndc.eventhandler.NdcEventPublisher;
import it.gov.innovazione.ndc.gen.dto.AssetType;
import it.gov.innovazione.ndc.gen.dto.SearchResult;
import it.gov.innovazione.ndc.gen.dto.SearchResultItem;
import it.gov.innovazione.ndc.harvester.SemanticAssetType;
import it.gov.innovazione.ndc.harvester.util.FileUtils;
import it.gov.innovazione.ndc.model.profiles.NDC;
import it.gov.innovazione.ndc.service.GithubService;
import junit.framework.AssertionFailedError;
Expand Down Expand Up @@ -39,6 +39,8 @@ public class RestApiIntegrationTests extends BaseIntegrationTest {

@MockBean
private GithubService githubService;
@MockBean
private NdcEventPublisher ndcEventPublisher;

@DynamicPropertySource
static void updateDynamicPropertySource(DynamicPropertyRegistry registry) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import it.gov.innovazione.ndc.controller.exception.VocabularyDataNotFoundException;
import it.gov.innovazione.ndc.controller.exception.VocabularyItemNotFoundException;
import it.gov.innovazione.ndc.eventhandler.NdcEventPublisher;
import it.gov.innovazione.ndc.gen.dto.VocabularyData;
import it.gov.innovazione.ndc.harvester.csv.CsvParser;
import it.gov.innovazione.ndc.integration.Containers;
Expand Down Expand Up @@ -40,6 +41,9 @@ public class VocabularyDataServiceIntegrationTest {
@MockBean
private GithubService githubService;

@MockBean
private NdcEventPublisher eventPublisher;

@DynamicPropertySource
static void updateTestcontainersProperties(DynamicPropertyRegistry registry) {
registry.add("spring.elasticsearch.rest.uris", elasticsearchContainer::getHttpHostAddress);
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/it/gov/innovazione/ndc/alerter/AlerterService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package it.gov.innovazione.ndc.alerter;

import it.gov.innovazione.ndc.alerter.data.EventService;
import it.gov.innovazione.ndc.alerter.dto.EventDto;
import it.gov.innovazione.ndc.alerter.event.AlertableEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.time.Instant;

import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;

@Service
@RequiredArgsConstructor
public class AlerterService {

private final EventService eventService;

public void alert(AlertableEvent alertableEvent) {
eventService.create(EventDto.builder()
.name(alertableEvent.getName())
.description(alertableEvent.getDescription())
.category(alertableEvent.getCategory())
.context(alertableEvent.getContext())
.severity(alertableEvent.getSeverity())
.createdBy(getUser())
.occurredAt(defaultIfNull(alertableEvent.getOccurredAt(), Instant.now()))
.build());
}

private String getUser() {
return defaultIfNull(SecurityContextHolder.getContext().getAuthentication().getName(), "system");

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@RestController
@RequiredArgsConstructor
@RequestMapping("/event")
Expand All @@ -25,12 +22,6 @@ public class EventController extends AbstractCrudController<Event, EventDto> {
@Getter(AccessLevel.PROTECTED)
private final EventMapper entityMapper;

@Override
public EventDto create(@Valid @RequestBody EventDto entity) {
// todo: logic to handle the event
return super.create(entity);
}

@Override
protected void handlePreUpdate(EventDto entity) {
throw IMMUTABLE_EXCEPTION;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.List;
import java.util.stream.Collectors;

@RequiredArgsConstructor
public abstract class EntityService<T extends Nameable, D extends Nameable> {

Expand Down Expand Up @@ -88,6 +92,13 @@ private ConflictingOperationException entityDoesNotExistsException() {
return new ConflictingOperationException(getEntityName() + " does not exist");
}

@Transactional(readOnly = true)
public List<D> findAll() {
return getRepository().findAll().stream()
.map(a -> getEntityMapper().toDto(a))
.collect(Collectors.toList());
}

@ResponseStatus(HttpStatus.CONFLICT)
public static class ConflictingOperationException extends RuntimeException {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
import org.springframework.stereotype.Repository;

import java.time.Instant;
import java.util.List;

@Repository
interface EventRepository extends NameableRepository<Event, String> {

boolean existsByNameAndOccurredAt(String name, Instant occurredAt);

List<Event> findByCreatedAtAfter(Instant instant);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.List;

import static java.util.stream.Collectors.toList;

@Service
@RequiredArgsConstructor
public class EventService extends EntityService<Event, EventDto> {
Expand All @@ -30,4 +35,10 @@ protected void assertEntityDoesNotExists(EventDto dto) {
throw new ConflictingOperationException("An event with the same name/occurredAt already exists: " + dto.getName() + "/" + dto.getOccurredAt());
}
}

public List<EventDto> getEventsNewerThan(Instant instant) {
return repository.findByCreatedAtAfter(instant).stream()
.map(entityMapper::toDto)
.collect(toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void init() {
.name(pair.getLeft())
.eventCategories(pair.getRight())
.minSeverity(Severity.INFO)
.aggregationTime(60)
.aggregationTime(60L)
.build())
.forEach(p -> {
log.info("Creating default profile: {}", p.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import it.gov.innovazione.ndc.alerter.entities.Profile;
import lombok.SneakyThrows;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -14,6 +15,10 @@
@Mapper(componentModel = "spring")
public abstract class ProfileMapper implements EntityMapper<Profile, ProfileDto> {

@Override
@Mapping(target = "lastAlertedAt", ignore = true)
public abstract Profile toEntity(ProfileDto dto);

@SneakyThrows
protected List<EventCategory> stringListToEventCategoryList(List<String> list) {
if (list == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.time.Duration;
import java.time.Instant;

@Service
@RequiredArgsConstructor
Expand All @@ -28,4 +30,18 @@ public class ProfileService extends EntityService<Profile, ProfileDto> {

@Getter(AccessLevel.PROTECTED)
private final Sort defaultSorting = Sort.by("name").ascending();

public void setLastUpdated(String id) {
Profile profile = repository.findById(id)
.orElseThrow(() -> new IllegalStateException("Profile not found: " + id));
profile.setLastAlertedAt(Instant.now());
repository.save(profile);
}

public void setAllLastUpdated(Duration backoff) {
repository.findAll().forEach(profile -> {
profile.setLastAlertedAt(Instant.now().minus(backoff));
repository.save(profile);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
@Repository
interface UserRepository extends NameableRepository<User, String> {

boolean existsByNameAndSurnameAndEmail(String name, String surname, String email);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,12 @@ public class UserService extends EntityService<User, UserDto> {
private final String entityName = "User";
@Getter(AccessLevel.PROTECTED)
private final Sort defaultSorting = Sort.by("name").ascending();

@Override
protected void assertEntityDoesNotExists(UserDto dto) {
if (repository.existsByNameAndSurnameAndEmail(
dto.getName(), dto.getSurname(), dto.getEmail())) {
throw new ConflictingOperationException("An user with the same name/surname/email already exists: " + dto.getName() + "/" + dto.getSurname() + "/" + dto.getEmail());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import it.gov.innovazione.ndc.alerter.entities.EventCategory;
import it.gov.innovazione.ndc.alerter.entities.Nameable;
import it.gov.innovazione.ndc.alerter.entities.Severity;
import lombok.Builder;
import lombok.Data;

import javax.validation.constraints.NotBlank;
Expand All @@ -11,6 +12,7 @@
import java.util.Map;

@Data
@Builder
public class EventDto implements Nameable {
private String id;
@NotBlank(message = "Name is mandatory")
Expand All @@ -19,9 +21,13 @@ public class EventDto implements Nameable {
private String description;
@NotNull
private EventCategory category;
@Builder.Default
private Severity severity = Severity.INFO;
@Builder.Default
private Map<String, Object> context = Map.of();
@Builder.Default
private Instant occurredAt = Instant.now();
private Instant createdAt;
@Builder.Default
private String createdBy = "system";
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.time.Instant;
import java.util.List;

@Data
Expand All @@ -17,4 +18,5 @@ public class ProfileDto implements Nameable {
private List<String> eventCategories;
private Severity minSeverity = Severity.INFO;
private Integer aggregationTime = 60;
private Instant lastAlertedAt;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.time.Instant;
import java.util.List;

@Data
Expand All @@ -34,5 +35,8 @@ public class Profile implements Nameable {
@Column(nullable = false)
private Severity minSeverity;
@Column(nullable = false)
private Integer aggregationTime;
private Long aggregationTime;
@Column
@Builder.Default
private Instant lastAlertedAt = Instant.now();
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"name", "surname", "email"})})
public class User implements Nameable {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
private String id;
@Column(unique = true, nullable = false)
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String surname;
Expand Down
Loading

0 comments on commit 37810c2

Please sign in to comment.