Skip to content

Commit

Permalink
introduce timer type
Browse files Browse the repository at this point in the history
  • Loading branch information
dmtkachenko committed Dec 24, 2024
1 parent a2876e8 commit d6efd6c
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
import io.hypersistence.utils.hibernate.type.json.JsonBinaryType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.UUID;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.folio.scheduler.domain.dto.RoutingEntry;
import org.folio.scheduler.domain.dto.TimerDescriptor;
import org.folio.scheduler.domain.model.TimerType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.Type;
import org.hibernate.type.SqlTypes;

@Data
@Entity
Expand All @@ -20,6 +25,11 @@ public class TimerDescriptorEntity {

@Id private UUID id;

@Enumerated(EnumType.STRING)
@JdbcTypeCode(SqlTypes.NAMED_ENUM)
@Column(name = "type", columnDefinition = "timer_type")
private TimerType type;

private String moduleName;

private String moduleId;
Expand All @@ -35,23 +45,15 @@ public void setTimerDescriptor(TimerDescriptor timerDescriptor) {
this.naturalKey = toNaturalKey(timerDescriptor);
}

public static TimerDescriptorEntity of(TimerDescriptor timerDescriptor) {
var entity = new TimerDescriptorEntity();
entity.id = timerDescriptor.getId();
entity.timerDescriptor = timerDescriptor;
entity.naturalKey = toNaturalKey(timerDescriptor);
return entity;
}

public static String toNaturalKey(TimerDescriptor timerDescriptor) {
RoutingEntry re = timerDescriptor.getRoutingEntry();
public static String toNaturalKey(TimerDescriptor td) {
RoutingEntry re = td.getRoutingEntry();
if (re == null) {
return null;
}

var methods = re.getMethods() != null ? String.join(",", re.getMethods()) : "";
var path = re.getPath() != null ? re.getPath() : re.getPathPattern();
return String.format("%s#%s#%s", timerDescriptor.getModuleName() != null ? timerDescriptor.getModuleName() : "",
return String.format("%s#%s#%s#%s", td.getType(), td.getModuleName() != null ? td.getModuleName() : "",
methods, path);
}
}
6 changes: 6 additions & 0 deletions src/main/java/org/folio/scheduler/domain/model/TimerType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.folio.scheduler.domain.model;

public enum TimerType {
USER,
SYSTEM
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import static java.util.Collections.singletonList;
import static org.apache.commons.collections4.CollectionUtils.isEmpty;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import static org.folio.common.utils.CollectionUtils.mapItems;
import static org.folio.scheduler.domain.model.TimerType.SYSTEM;
import static org.folio.scheduler.utils.OkapiRequestUtils.getStaticPath;
import static org.folio.spring.integration.XOkapiHeaders.TENANT;
import static org.folio.spring.integration.XOkapiHeaders.USER_ID;
Expand All @@ -18,6 +20,7 @@
import org.folio.common.utils.SemverUtils;
import org.folio.scheduler.domain.dto.RoutingEntry;
import org.folio.scheduler.domain.dto.TimerDescriptor;
import org.folio.scheduler.domain.dto.TimerType;
import org.folio.scheduler.integration.kafka.model.ResourceEvent;
import org.folio.scheduler.integration.keycloak.SystemUserService;
import org.folio.scheduler.service.SchedulerTimerService;
Expand Down Expand Up @@ -77,7 +80,7 @@ private void updateTimers(ResourceEvent resourceEvent) {
var moduleId = resourceEvent.getNewValue().getModuleId();
var moduleName = SemverUtils.getName(moduleId);

var timers = schedulerTimerService.findByModuleName(moduleName);
var timers = schedulerTimerService.findByModuleNameAndType(moduleName, SYSTEM);
if (isNotEmpty(timers)) {
logDeletingTimers(timers);
for (var timer : timers) {
Expand All @@ -97,7 +100,7 @@ private void deleteTimers(ResourceEvent resourceEvent) {
try (var ignored = new FolioExecutionContextSetter(folioModuleMetadata,
prepareContextHeaders(resourceEvent.getTenant()))) {
var moduleName = SemverUtils.getName(resourceEvent.getOldValue().getModuleId());
var timers = schedulerTimerService.findByModuleName(moduleName);
var timers = schedulerTimerService.findByModuleNameAndType(moduleName, SYSTEM);
if (isEmpty(timers)) {
return;
}
Expand All @@ -118,6 +121,7 @@ private Map<String, Collection<String>> prepareContextHeaders(String tenant) {

private static TimerDescriptor createTimerDescriptor(RoutingEntry routingEntry, String moduleName, String moduleId) {
return new TimerDescriptor().enabled(TRUE)
.type(TimerType.SYSTEM)
.moduleName(moduleName)
.moduleId(moduleId)
.routingEntry(routingEntry);
Expand All @@ -129,12 +133,12 @@ private static String getRoutingEntryKey(RoutingEntry routingEntry) {
}

private static void logCreatingTimers(List<RoutingEntry> entries) {
var methods = entries.stream().map(KafkaMessageListener::getRoutingEntryKey).toList();
log.info("Processing scheduled job event from kafka: timers = {}", methods);
log.info("Processing scheduled job event from kafka: timers = {}",
() -> mapItems(entries, KafkaMessageListener::getRoutingEntryKey));
}

private static void logDeletingTimers(List<TimerDescriptor> timers) {
var methods = timers.stream().map(t -> getRoutingEntryKey(t.getRoutingEntry())).toList();
log.info("Deleting timers: timers = {}", methods);
log.info("Deleting timers: timers = {}",
() -> mapItems(timers, t -> getRoutingEntryKey(t.getRoutingEntry())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import java.util.Optional;
import java.util.UUID;
import org.folio.scheduler.domain.entity.TimerDescriptorEntity;
import org.folio.scheduler.domain.model.TimerType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface SchedulerTimerRepository extends JpaRepository<TimerDescriptorEntity, UUID> {

List<TimerDescriptorEntity> findByModuleName(String moduleName);
List<TimerDescriptorEntity> findByModuleNameAndType(String moduleName, TimerType type);

Optional<TimerDescriptorEntity> findByNaturalKey(String naturalKey);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.folio.scheduler.domain.dto.TimerDescriptor;
import org.folio.scheduler.domain.entity.TimerDescriptorEntity;
import org.folio.scheduler.domain.model.SearchResult;
import org.folio.scheduler.domain.model.TimerType;
import org.folio.scheduler.exception.RequestValidationException;
import org.folio.scheduler.mapper.TimerDescriptorMapper;
import org.folio.scheduler.repository.SchedulerTimerRepository;
Expand Down Expand Up @@ -43,8 +44,9 @@ public Optional<TimerDescriptor> findById(UUID uuid) {
}

@Transactional(readOnly = true)
public List<TimerDescriptor> findByModuleName(String moduleName) {
return mapItems(schedulerTimerRepository.findByModuleName(moduleName), TimerDescriptorEntity::getTimerDescriptor);
public List<TimerDescriptor> findByModuleNameAndType(String moduleName, TimerType type) {
return mapItems(schedulerTimerRepository.findByModuleNameAndType(moduleName, type),
TimerDescriptorEntity::getTimerDescriptor);
}

/**
Expand Down Expand Up @@ -156,6 +158,10 @@ private void validate(TimerDescriptor timerDescriptor) {
if (isEmpty(timerDescriptor.getModuleId()) && isEmpty(timerDescriptor.getModuleName())) {
throw new IllegalArgumentException("Module id or module name is required");
}

if (timerDescriptor.getType() == null) {
throw new IllegalArgumentException("Timer type is required");
}
}

private TimerDescriptor doCreate(TimerDescriptor timerDescriptor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.folio.scheduler.configuration.properties.OkapiConfigurationProperties;
import org.folio.scheduler.domain.dto.RoutingEntry;
import org.folio.scheduler.domain.dto.TimerDescriptor;
import org.folio.scheduler.domain.dto.TimerType;
import org.folio.scheduler.integration.OkapiClient;
import org.folio.scheduler.service.SchedulerTimerService;
import org.folio.scheduler.service.UserImpersonationService;
Expand Down Expand Up @@ -115,7 +116,9 @@ private void callHttpMethod(TimerDescriptor timerDescriptor) {
}

private static String moduleHint(TimerDescriptor td) {
return StringUtils.isNotEmpty(td.getModuleId()) ? td.getModuleId() : td.getModuleName();
return td.getType() == TimerType.USER
? td.getModuleName()
: StringUtils.isNotEmpty(td.getModuleId()) ? td.getModuleId() : td.getModuleName();
}

private static String getStaticPath(RoutingEntry re) {
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/swagger.api/schemas/timerDescriptor.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
"type": "string",
"format": "uuid"
},
"type": {
"$ref": "timerType.json",
"description": "Timer type"
},
"modified": {
"description": "Whether modified",
"type": "boolean"
Expand Down
10 changes: 10 additions & 0 deletions src/main/resources/swagger.api/schemas/timerType.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "timerType.json",
"title": "Timer Type Schema",
"description": "Timer type",
"default": "user",
"type": "string",
"enum": [ "user", "system" ],
"x-enum-varnames": [ "USER", "SYSTEM" ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.folio.scheduler.domain.dto.TimerUnit.MINUTE;
import static org.folio.scheduler.domain.model.TimerType.SYSTEM;
import static org.folio.scheduler.integration.kafka.model.ResourceEventType.CREATE;
import static org.folio.scheduler.integration.kafka.model.ResourceEventType.DELETE;
import static org.folio.scheduler.integration.kafka.model.ResourceEventType.UPDATE;
Expand Down Expand Up @@ -63,29 +64,29 @@ void handleScheduledJobEvent_positive_create() {
@Test
void handleScheduledJobEvent_positive_update() {
when(systemUserService.findSystemUserId(TENANT_ID)).thenReturn(SYSTEM_USER_ID);
when(schedulerTimerService.findByModuleName(MODULE_NAME)).thenReturn(
when(schedulerTimerService.findByModuleNameAndType(MODULE_NAME, SYSTEM)).thenReturn(
List.of(new TimerDescriptor().id(TIMER_ID).enabled(true).routingEntry(routingEntry1())));

var consumerRec = new ConsumerRecord<>(TOPIC_NAME, 0, 0, TENANT_ID, udpateResourceEvent());
kafkaMessageListener.handleScheduledJobEvent(consumerRec);

verify(schedulerTimerService).delete(TIMER_ID);
verify(schedulerTimerService).findByModuleName(MODULE_NAME);
verify(schedulerTimerService).findByModuleNameAndType(MODULE_NAME, SYSTEM);
verify(schedulerTimerService).create(
new TimerDescriptor().enabled(true).moduleName(MODULE_NAME).moduleId(MODULE_ID2).routingEntry(routingEntry2()));
}

@Test
void handleScheduledJobEvent_positive_delete() {
when(systemUserService.findSystemUserId(TENANT_ID)).thenReturn(SYSTEM_USER_ID);
when(schedulerTimerService.findByModuleName(MODULE_NAME)).thenReturn(
when(schedulerTimerService.findByModuleNameAndType(MODULE_NAME, SYSTEM)).thenReturn(
List.of(new TimerDescriptor().id(TIMER_ID).enabled(true).routingEntry(routingEntry1())));

var consumerRec = new ConsumerRecord<>(TOPIC_NAME, 0, 0, TENANT_ID, deleteResourceEvent());
kafkaMessageListener.handleScheduledJobEvent(consumerRec);

verify(schedulerTimerService).delete(TIMER_ID);
verify(schedulerTimerService).findByModuleName(MODULE_NAME);
verify(schedulerTimerService).findByModuleNameAndType(MODULE_NAME, SYSTEM);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static java.util.List.of;
import static org.assertj.core.api.Assertions.assertThat;
import static org.folio.scheduler.domain.entity.TimerDescriptorEntity.toNaturalKey;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -67,9 +68,9 @@ void test_execute_positive() throws Exception {

when(mockResultSet.getString("id")).thenReturn(uuid1.toString()).thenReturn(uuid2.toString());
when(mockSchedulerTimerRepository.findById(uuid1)).thenReturn(
Optional.of(TimerDescriptorEntity.of(new TimerDescriptor(routingEntry1, true))));
Optional.of(entity(new TimerDescriptor(routingEntry1, true))));
when(mockSchedulerTimerRepository.findById(uuid2)).thenReturn(
Optional.of(TimerDescriptorEntity.of(new TimerDescriptor(routingEntry2, false))));
Optional.of(entity(new TimerDescriptor(routingEntry2, false))));

var methodsPassed = new ArrayList<>();
when(mockSchedulerTimerRepository.save(any())).thenAnswer(inv -> {
Expand Down Expand Up @@ -113,4 +114,12 @@ protected Database setupDbConnectionMock(Map<String, ResultSet> mockQueryRespons
}
return mockLiquibaseDbAccess;
}

private static TimerDescriptorEntity entity(TimerDescriptor timerDescriptor) {
var entity = new TimerDescriptorEntity();
entity.setId(timerDescriptor.getId());
entity.setTimerDescriptor(timerDescriptor);
entity.setNaturalKey(toNaturalKey(timerDescriptor));
return entity;
}
}

0 comments on commit d6efd6c

Please sign in to comment.