Skip to content

Commit

Permalink
DTSAM-317 Update processing of REFRESH_JOB env var (#1923)
Browse files Browse the repository at this point in the history
* DTSAM-263 Update processing of REFRESH_JOB env var

   [DTSAM-263](https://tools.hmcts.net/jira/browse/DTSAM-263)
   [AM-2902](https://tools.hmcts.net/jira/browse/AM-2902)

Import some elements of AM-2902:
* Update the processing of the REFRESH_JOB env variable on start up to
  allow it to generate multiple refresh jobs at a time.

* DTSAM-317 Update processing of REFRESH_JOB env var

   [DTSAM-263](https://tools.hmcts.net/jira/browse/DTSAM-263)
   [DTSAM-317](https://tools.hmcts.net/jira/browse/DTSAM-317)

Prevent unexpected update of job config during startup.

---------

Co-authored-by: mikebrownccd <104495891+mikebrownccd@users.noreply.github.com>
  • Loading branch information
mattnayler and mikebrownccd authored May 30, 2024
1 parent 1d282f6 commit 4f676b7
Show file tree
Hide file tree
Showing 7 changed files with 804 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package uk.gov.hmcts.reform.orgrolemapping.domain.service;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.jdbc.Sql;
import uk.gov.hmcts.reform.orgrolemapping.controller.BaseTestIntegration;
import uk.gov.hmcts.reform.orgrolemapping.controller.advice.exception.UnprocessableEntityException;
import uk.gov.hmcts.reform.orgrolemapping.data.RefreshJobEntity;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

@SuppressWarnings("UnnecessaryLocalVariable")
class RefreshJobConfigServiceIntegrationTest extends BaseTestIntegration {

private static final String JOB_CONFIG_1 = "LEGAL_OPERATIONS-PUBLICLAW-NEW-0";
private static final String JOB_CONFIG_2 = "JUDICIAL-CIVIL-ABORTED-4";
private static final String JOB_CONFIG_BAD = "BAD_JOB-INCORRECT_FORMAT-NOT_ENOUGH_PARTS";

private static final Long NEXT_JOB_ID = 5L; // NB: this is next sequence number in `sql/insert_refresh_jobs.sql`

@Autowired
private PersistenceService persistenceService;

@Autowired
private RefreshJobConfigService sut;

@Test
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_refresh_jobs.sql"})
void testProcessJobDetail_singleJob_noJobId() {

// GIVEN
String jobDetail = JOB_CONFIG_1;

// WHEN
sut.processJobDetail(jobDetail, false);

// THEN
var jobOutput = persistenceService.fetchRefreshJobById(NEXT_JOB_ID);
assertTrue(jobOutput.isPresent());
assertJobEqualsJobConfig1(jobOutput.get());
}

@Test
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_refresh_jobs.sql"})
void testProcessJobDetail_singleJob_withJobId_new() {

// GIVEN
String jobDetail = createJobConfigWithId(JOB_CONFIG_1, NEXT_JOB_ID);

// WHEN
sut.processJobDetail(jobDetail, false);

// THEN
var jobOutput = persistenceService.fetchRefreshJobById(NEXT_JOB_ID);
assertTrue(jobOutput.isPresent());
assertEquals(NEXT_JOB_ID, jobOutput.get().getJobId());
assertJobEqualsJobConfig1(jobOutput.get());
}

@Test
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_refresh_jobs.sql"})
void testProcessJobDetail_singleJob_withJobId_new_mismatched() {

// GIVEN
String jobDetail = createJobConfigWithId(JOB_CONFIG_1, NEXT_JOB_ID + 1);

// WHEN / THEN
UnprocessableEntityException exception = assertThrows(UnprocessableEntityException.class, () ->
sut.processJobDetail(jobDetail, false)
);

// THEN
assertTrue(exception.getMessage().startsWith(RefreshJobConfigService.ERROR_REJECTED_JOB_ID_MISMATCH));
// verify new job config entry not created
assertTrue(persistenceService.fetchRefreshJobById(NEXT_JOB_ID).isEmpty()); // i.e. mismatched removed
assertTrue(persistenceService.fetchRefreshJobById(NEXT_JOB_ID + 1).isEmpty()); // requested not created
}

@Test
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_refresh_jobs.sql"})
void testProcessJobDetail_multipleJobs_withJobId_updateAllowed() {

// GIVEN
String jobDetail = createMultipleJobDetailsList(
createJobConfigWithId(JOB_CONFIG_1, 1L), // this is an update 1 < NEXT_JOB_ID
createJobConfigWithId(JOB_CONFIG_2, 2L) // this is an update 2 < NEXT_JOB_ID
);

// WHEN
sut.processJobDetail(jobDetail, true); // NB: allows update

// THEN
// verify updated job is saved OK
var jobOutput1 = persistenceService.fetchRefreshJobById(1L);
assertTrue(jobOutput1.isPresent());
assertEquals(1L, jobOutput1.get().getJobId());
assertJobEqualsJobConfig1(jobOutput1.get());

// verify updated job is saved OK
var jobOutput2 = persistenceService.fetchRefreshJobById(2L);
assertTrue(jobOutput2.isPresent());
assertEquals(2L, jobOutput2.get().getJobId());
assertJobEqualsJobConfig2(jobOutput2.get());
}

@Test
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_refresh_jobs.sql"})
void testProcessJobDetail_multipleJobs_withJobId_noUpdateAllowed_includingSkipped() {

// GIVEN
String jobDetail = createMultipleJobDetailsList(
createJobConfigWithId(JOB_CONFIG_1, 4L), // this update will be skipped
createJobConfigWithId(JOB_CONFIG_2, NEXT_JOB_ID) // this new job will be saved
);

// WHEN
sut.processJobDetail(jobDetail, false);

// THEN
// verify update not applied
var jobOutput1 = persistenceService.fetchRefreshJobById(4L);
assertTrue(jobOutput1.isPresent());
assertJobEqualsJob4Unchanged(jobOutput1.get());

// verify new job is saved OK
var jobOutput2 = persistenceService.fetchRefreshJobById(NEXT_JOB_ID);
assertTrue(jobOutput2.isPresent());
assertEquals(NEXT_JOB_ID, jobOutput2.get().getJobId());
assertJobEqualsJobConfig2(jobOutput2.get());
}

@Test
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:sql/insert_refresh_jobs.sql"})
void testProcessJobDetail_multipleJobs_badJob_noSaveOrUpdate() {

// GIVEN
String jobDetail = createMultipleJobDetailsList(
createJobConfigWithId(JOB_CONFIG_1, 4L), // this is an update
createJobConfigWithId(JOB_CONFIG_2, NEXT_JOB_ID), // this is a new job
JOB_CONFIG_BAD
);

// WHEN / THEN
assertThrows(UnprocessableEntityException.class, () ->
sut.processJobDetail(jobDetail, true) // NB: allows update
);

// THEN
// verify update has been rolled back
var jobOutput1 = persistenceService.fetchRefreshJobById(4L);
assertTrue(jobOutput1.isPresent());
assertJobEqualsJob4Unchanged(jobOutput1.get());

// verify new job has been rolled back
assertTrue(persistenceService.fetchRefreshJobById(NEXT_JOB_ID).isEmpty());
}

private void assertJobEqualsJobConfig1(RefreshJobEntity job) {
// assert entries match: JOB_CONFIG_1
assertEquals("LEGAL_OPERATIONS", job.getRoleCategory());
assertEquals("PUBLICLAW", job.getJurisdiction());
assertEquals("NEW", job.getStatus());
assertEquals(0, job.getLinkedJobId());
}

private void assertJobEqualsJobConfig2(RefreshJobEntity job) {
// assert entries match: JOB_CONFIG_2
assertEquals("JUDICIAL", job.getRoleCategory());
assertEquals("CIVIL", job.getJurisdiction());
assertEquals("ABORTED", job.getStatus());
assertEquals(4, job.getLinkedJobId());
}

private void assertJobEqualsJob4Unchanged(RefreshJobEntity job) {
// verify update not applied: i.e. must match jobId=4 in `sql/insert_refresh_jobs.sql`
assertEquals(4L, job.getJobId());
assertEquals("LEGAL_OPERATIONS", job.getRoleCategory());
assertEquals("IA", job.getJurisdiction());
assertEquals("COMPLETED", job.getStatus());
assertEquals(2, job.getLinkedJobId());
}

private String createJobConfigWithId(String jobConfig, Long jobId) {
return jobConfig + RefreshJobConfigService.REFRESH_JOBS_CONFIG_SPLITTER + jobId;
}

private String createMultipleJobDetailsList(String... jobConfigs) {
return String.join(RefreshJobConfigService.REFRESH_JOBS_DETAILS_SPLITTER, jobConfigs);
}

}
12 changes: 12 additions & 0 deletions src/integrationTest/resources/sql/insert_refresh_jobs.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
DELETE FROM refresh_jobs;

INSERT INTO public.refresh_jobs (job_id, role_category, jurisdiction, status, user_ids, linked_job_id, created)
VALUES(1, 'LEGAL_OPERATIONS', 'IA', 'NEW', NULL, 0, NULL);
INSERT INTO public.refresh_jobs (job_id, role_category, jurisdiction, status, user_ids, linked_job_id, created)
VALUES(2, 'LEGAL_OPERATIONS', 'IA', 'ABORTED', '{"7c12a4bc-450e-4290-8063-b387a5d5e0b7"}', NULL, NULL);
INSERT INTO public.refresh_jobs (job_id, role_category, jurisdiction, status, user_ids, linked_job_id, created)
VALUES(3, 'LEGAL_OPERATIONS', 'IA', 'NEW', NULL, 2, NULL);
INSERT INTO public.refresh_jobs (job_id, role_category, jurisdiction, status, user_ids, linked_job_id, created)
VALUES(4, 'LEGAL_OPERATIONS', 'IA', 'COMPLETED', NULL, 2, NULL);

ALTER SEQUENCE JOB_ID_SEQ RESTART WITH 5;
Original file line number Diff line number Diff line change
@@ -1,67 +1,58 @@
package uk.gov.hmcts.reform.orgrolemapping.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import uk.gov.hmcts.reform.orgrolemapping.data.RefreshJobEntity;
import uk.gov.hmcts.reform.orgrolemapping.data.RefreshJobsRepository;
import uk.gov.hmcts.reform.orgrolemapping.controller.advice.exception.UnprocessableEntityException;
import uk.gov.hmcts.reform.orgrolemapping.domain.service.RefreshJobConfigService;
import uk.gov.hmcts.reform.orgrolemapping.launchdarkly.FeatureConditionEvaluator;

import java.time.ZonedDateTime;
import java.util.Optional;

@Component
@Slf4j
public class JobConfiguration implements CommandLineRunner {

private final RefreshJobsRepository refreshJobsRepository;
static final String ERROR_ABORTED_JOB_IMPORT = "Aborted the job configuration import: {}";

private final RefreshJobConfigService refreshJobConfigService;

private final FeatureConditionEvaluator featureConditionEvaluator;

private final String jobDetail;
private final boolean jobDetailAllowUpdate;


@Autowired
public JobConfiguration(RefreshJobsRepository refreshJobsRepository,
public JobConfiguration(RefreshJobConfigService refreshJobConfigService,
@Value("${refresh.job.update}") String jobDetail,
@Value("${refresh.Job.updateOverride}") Boolean jobDetailAllowUpdate,
FeatureConditionEvaluator featureConditionEvaluator
) {
this.refreshJobsRepository = refreshJobsRepository;
this.refreshJobConfigService = refreshJobConfigService;
this.featureConditionEvaluator = featureConditionEvaluator;
this.jobDetail = jobDetail;
this.jobDetailAllowUpdate = BooleanUtils.isTrue(jobDetailAllowUpdate);
}

@Override
public void run(String... args) {
if (StringUtils.isNotEmpty(jobDetail) && featureConditionEvaluator
.isFlagEnabled("am_org_role_mapping_service", "orm-refresh-job-enable")) {
String[] jobAttributes = jobDetail.split("-");
log.info("Job {} inserting into refresh table", jobDetail);
if (jobAttributes.length < 4) {
return;
}
RefreshJobEntity refreshJobEntity = RefreshJobEntity.builder().build();
if (jobAttributes.length > 4) {
Optional<RefreshJobEntity> refreshJob = refreshJobsRepository.findById(Long.valueOf(jobAttributes[4]));
refreshJobEntity = refreshJob.orElse(refreshJobEntity);
}

refreshJobEntity.setRoleCategory(jobAttributes[0]);
refreshJobEntity.setJurisdiction(jobAttributes[1]);
refreshJobEntity.setStatus(jobAttributes[2]);
refreshJobEntity.setLinkedJobId(Long.valueOf(jobAttributes[3]));
refreshJobEntity.setCreated(ZonedDateTime.now());

persistJobDetail(refreshJobEntity);
if (StringUtils.isNotEmpty(jobDetail)) {
if (featureConditionEvaluator.isFlagEnabled("am_org_role_mapping_service", "orm-refresh-job-enable")) {
try {
this.refreshJobConfigService.processJobDetail(this.jobDetail, this.jobDetailAllowUpdate);
} catch (UnprocessableEntityException ex) {
log.error(ERROR_ABORTED_JOB_IMPORT, ex.getMessage(), ex);
}
} else {
log.warn("LD flag 'orm-refresh-job-enable' is not enabled");
}
} else {
log.warn("LD flag 'orm-refresh-job-enable' is not enabled");
log.info("No Job Configuration to create");
}
}

private void persistJobDetail(RefreshJobEntity refreshJobEntity) {
refreshJobsRepository.save(refreshJobEntity);
}
}
Loading

0 comments on commit 4f676b7

Please sign in to comment.