diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..88bf97c508 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/pom.xml b/pom.xml index f82a63ccb3..add25b9214 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,5 @@ - + 4.0.0 @@ -122,7 +122,7 @@ (&(objectClass=person)(cn=%s)) true - 30000 + 30000 public (&(objectClass=person)(userPrincipalName=%s)) displayname @@ -191,8 +191,8 @@ - true - + true + 8080 @@ -216,7 +216,7 @@ false PBEWithMD5AndDES - + OHDSI @@ -298,6 +298,7 @@ /tmp/atlas/audit/audit.log /tmp/atlas/audit/audit-extra.log + WebAPI ${basedir}/src/main/java @@ -368,8 +369,8 @@ - git.branch - git.commit.id.abbrev + git.branch + git.commit.id.abbrev @@ -575,6 +576,7 @@ + com.fasterxml.jackson.core @@ -808,12 +810,12 @@ jackson-databind - org.slf4j - slf4j-log4j12 + org.slf4j + slf4j-log4j12 - log4j - log4j + log4j + log4j @@ -1159,7 +1161,7 @@ ${pac4j.version} - com.fasterxml.jackson.core + com.fasterxml.jackson.core jackson-databind @@ -1205,12 +1207,12 @@ spring-boot-starter-test ${spring.boot.version} test - - - com.vaadin.external.google - android-json - - + + + com.vaadin.external.google + android-json + + org.dbunit @@ -1231,12 +1233,13 @@ test - com.github.mjeanroy - dbunit-plus - 2.0.1 - test + com.github.mjeanroy + dbunit-plus + 2.0.1 + test + webapi-oracle @@ -1329,17 +1332,17 @@ lower(email) = lower(?) - + ohdsi.snapshots repo.ohdsi.org-snapshots https://repo.ohdsi.org/nexus/content/repositories/snapshots false - + true - - + + @@ -1412,7 +1415,7 @@ true 2.6.15 - ...path/to/impala/jdbc/drivers... + ...path/to/impala/jdbc/drivers... @@ -1517,9 +1520,9 @@ v2-rev20220326-1.32.1 - com.google.cloud - google-cloud-bigquery - 1.2.15 + com.google.cloud + google-cloud-bigquery + 1.2.15 com.google.http-client diff --git a/src/main/java/org/ohdsi/webapi/achilles/repository/AchillesCacheRepository.java b/src/main/java/org/ohdsi/webapi/achilles/repository/AchillesCacheRepository.java index 2479cc7da7..9e5e8c3805 100644 --- a/src/main/java/org/ohdsi/webapi/achilles/repository/AchillesCacheRepository.java +++ b/src/main/java/org/ohdsi/webapi/achilles/repository/AchillesCacheRepository.java @@ -2,7 +2,7 @@ import org.ohdsi.webapi.achilles.domain.AchillesCacheEntity; import org.ohdsi.webapi.source.Source; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; @@ -16,4 +16,8 @@ public interface AchillesCacheRepository extends CrudRepository findBySourceAndNames(@Param("source") Source source, @Param("names") List names); + + @Modifying + @Query("delete from AchillesCacheEntity where source = :source") + void deleteBySource(@Param("source") Source source); } diff --git a/src/main/java/org/ohdsi/webapi/achilles/service/AchillesCacheService.java b/src/main/java/org/ohdsi/webapi/achilles/service/AchillesCacheService.java index c55180929b..023f6d7d69 100644 --- a/src/main/java/org/ohdsi/webapi/achilles/service/AchillesCacheService.java +++ b/src/main/java/org/ohdsi/webapi/achilles/service/AchillesCacheService.java @@ -5,9 +5,12 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.ohdsi.webapi.achilles.domain.AchillesCacheEntity; import org.ohdsi.webapi.achilles.repository.AchillesCacheRepository; +import org.ohdsi.webapi.shiro.management.datasource.SourceAccessor; import org.ohdsi.webapi.source.Source; +import org.ohdsi.webapi.source.SourceRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -22,103 +25,124 @@ @Service public class AchillesCacheService { - private static final Logger LOG = LoggerFactory.getLogger(AchillesCacheService.class); - - private final AchillesCacheRepository cacheRepository; - - private final ObjectMapper objectMapper; - - @Value("${spring.jpa.properties.hibernate.jdbc.batch_size}") - private int batchSize; - - public AchillesCacheService(AchillesCacheRepository cacheRepository, - ObjectMapper objectMapper) { - this.cacheRepository = cacheRepository; - this.objectMapper = objectMapper; - } - - @Transactional(readOnly = true) - public AchillesCacheEntity getCache(Source source, String cacheName) { - return cacheRepository.findBySourceAndCacheName(source, cacheName); - } - - @Transactional(readOnly = true) - public List findBySourceAndNames(Source source, List names) { - return cacheRepository.findBySourceAndNames(source, names); + private static final Logger LOG = LoggerFactory.getLogger(AchillesCacheService.class); + + private final AchillesCacheRepository cacheRepository; + + private final ObjectMapper objectMapper; + + @Value("${spring.jpa.properties.hibernate.jdbc.batch_size}") + private int batchSize; + + @Autowired + private SourceRepository sourceRepository; + + @Autowired + private SourceAccessor sourceAccessor; + + public AchillesCacheService(AchillesCacheRepository cacheRepository, + ObjectMapper objectMapper) { + this.cacheRepository = cacheRepository; + this.objectMapper = objectMapper; + } + + @Transactional(readOnly = true) + public AchillesCacheEntity getCache(Source source, String cacheName) { + return cacheRepository.findBySourceAndCacheName(source, cacheName); + } + + @Transactional(readOnly = true) + public List findBySourceAndNames(Source source, List names) { + return cacheRepository.findBySourceAndNames(source, names); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public AchillesCacheEntity createCache(Source source, String cacheName, Object result) + throws JsonProcessingException { + AchillesCacheEntity cacheEntity = getCache(source, cacheName); + String cache = objectMapper.writeValueAsString(result); + if (Objects.nonNull(cacheEntity)) { + cacheEntity.setCache(cache); + } else { + cacheEntity = new AchillesCacheEntity(); + cacheEntity.setSource(source); + cacheEntity.setCacheName(cacheName); + cacheEntity.setCache(cache); } - @Transactional(propagation = Propagation.REQUIRES_NEW) - public AchillesCacheEntity createCache(Source source, String cacheName, Object result) throws JsonProcessingException { - AchillesCacheEntity cacheEntity = getCache(source, cacheName); - String cache = objectMapper.writeValueAsString(result); - if (Objects.nonNull(cacheEntity)) { - cacheEntity.setCache(cache); - } else { - cacheEntity = new AchillesCacheEntity(); - cacheEntity.setSource(source); - cacheEntity.setCacheName(cacheName); - cacheEntity.setCache(cache); - } + return cacheRepository.save(cacheEntity); + } - return cacheRepository.save(cacheEntity); + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void saveDrilldownCacheMap(Source source, String domain, Map conceptNodes) { + if (conceptNodes.isEmpty()) { // nothing to cache + LOG.warn( + "Cannot cache drilldown reports for {}, domain {}. Check if result schema contains achilles results tables.", + source.getSourceKey(), domain); + return; } - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void saveDrilldownCacheMap(Source source, String domain, Map conceptNodes) { - if (conceptNodes.isEmpty()) { // nothing to cache - LOG.warn("Cannot cache drilldown reports for {}, domain {}. Check if result schema contains achilles results tables.", - source.getSourceKey(), domain); - return; - } - - Map nodes = new HashMap<>(batchSize); - for (Map.Entry entry : conceptNodes.entrySet()) { - if (nodes.size() >= batchSize) { - createCacheEntities(source, nodes); - nodes.clear(); - } - Integer key = entry.getKey(); - String cacheName = getCacheName(domain, key); - nodes.put(cacheName, entry.getValue()); - } + Map nodes = new HashMap<>(batchSize); + for (Map.Entry entry : conceptNodes.entrySet()) { + if (nodes.size() >= batchSize) { createCacheEntities(source, nodes); nodes.clear(); + } + Integer key = entry.getKey(); + String cacheName = getCacheName(domain, key); + nodes.put(cacheName, entry.getValue()); } - - private void createCacheEntities(Source source, Map nodes) { - List cacheEntities = getEntities(source, nodes); - cacheRepository.save(cacheEntities); - } - - private List getEntities(Source source, Map nodes) { - List cacheNames = new ArrayList<>(nodes.keySet()); - List cacheEntities = findBySourceAndNames(source, cacheNames); - nodes.forEach((key, value) -> { - // check if the entity with given cache name already exists - Optional cacheEntity = cacheEntities.stream() - .filter(entity -> entity.getCacheName().equals(key)) - .findAny(); - try { - String newValue = objectMapper.writeValueAsString(value); - if (cacheEntity.isPresent()) { - // if cache entity already exists update its value - cacheEntity.get().setCache(newValue); - } else { - // if cache entity does not exist - create new one - AchillesCacheEntity newEntity = new AchillesCacheEntity(); - newEntity.setCacheName(key); - newEntity.setSource(source); - newEntity.setCache(newValue); - cacheEntities.add(newEntity); - } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }); - return cacheEntities; - } - - private String getCacheName(String domain, int conceptId) { - return String.format("drilldown_%s_%d", domain, conceptId); + createCacheEntities(source, nodes); + nodes.clear(); + } + + @Transactional() + public void clearCache() { + List sources = sourceRepository.findAll(); + sources.stream().forEach(this::clearCache); + } + + @Transactional() + public void clearCache(Source source) { + if (sourceAccessor.hasAccess(source)) { + cacheRepository.deleteBySource(source); } + } + + private void createCacheEntities(Source source, Map nodes) { + List cacheEntities = getEntities(source, nodes); + cacheRepository.save(cacheEntities); + } + + private List getEntities(Source source, Map nodes) { + List cacheNames = new ArrayList<>(nodes.keySet()); + List cacheEntities = findBySourceAndNames(source, cacheNames); + nodes.forEach((key, value) -> { + // check if the entity with given cache name already exists + Optional cacheEntity = cacheEntities.stream() + .filter(entity -> entity.getCacheName().equals(key)) + .findAny(); + try { + String newValue = objectMapper.writeValueAsString(value); + if (cacheEntity.isPresent()) { + // if cache entity already exists update its value + cacheEntity.get().setCache(newValue); + } else { + // if cache entity does not exist - create new one + AchillesCacheEntity newEntity = new AchillesCacheEntity(); + newEntity.setCacheName(key); + newEntity.setSource(source); + newEntity.setCache(newValue); + cacheEntities.add(newEntity); + } + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }); + return cacheEntities; + } + + private String getCacheName(String domain, int conceptId) { + return String.format("drilldown_%s_%d", domain, conceptId); + } } diff --git a/src/main/java/org/ohdsi/webapi/cdmresults/repository/CDMCacheRepository.java b/src/main/java/org/ohdsi/webapi/cdmresults/repository/CDMCacheRepository.java index f329ddfcd2..140bc48bfa 100644 --- a/src/main/java/org/ohdsi/webapi/cdmresults/repository/CDMCacheRepository.java +++ b/src/main/java/org/ohdsi/webapi/cdmresults/repository/CDMCacheRepository.java @@ -1,6 +1,7 @@ package org.ohdsi.webapi.cdmresults.repository; import org.ohdsi.webapi.cdmresults.domain.CDMCacheEntity; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; @@ -12,4 +13,8 @@ public interface CDMCacheRepository extends CrudRepository { @Query("select c from CDMCacheEntity c where c.sourceId = :sourceId and c.conceptId in :conceptIds") List findBySourceAndConceptIds(@Param("sourceId") int sourceId, @Param("conceptIds") List conceptIds); + + @Modifying + @Query("delete from CDMCacheEntity c where c.sourceId = :sourceId") + void deleteBySource(@Param("sourceId") int sourceId); } diff --git a/src/main/java/org/ohdsi/webapi/cdmresults/service/CDMCacheService.java b/src/main/java/org/ohdsi/webapi/cdmresults/service/CDMCacheService.java index e3870609d4..8b7a6e520b 100644 --- a/src/main/java/org/ohdsi/webapi/cdmresults/service/CDMCacheService.java +++ b/src/main/java/org/ohdsi/webapi/cdmresults/service/CDMCacheService.java @@ -10,15 +10,18 @@ import org.ohdsi.webapi.cdmresults.mapper.DescendantRecordCountMapper; import org.ohdsi.webapi.cdmresults.repository.CDMCacheRepository; import org.ohdsi.webapi.service.AbstractDaoService; +import org.ohdsi.webapi.shiro.management.datasource.SourceAccessor; import org.ohdsi.webapi.source.Source; import org.ohdsi.webapi.source.SourceDaimon; import org.ohdsi.webapi.util.PreparedSqlRender; import org.ohdsi.webapi.util.PreparedStatementRenderer; import org.ohdsi.webapi.util.SessionUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.convert.ConversionService; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; @@ -51,6 +54,9 @@ public class CDMCacheService extends AbstractDaoService { private final ConversionService conversionService; + @Autowired + private SourceAccessor sourceAccessor; + public CDMCacheService(CDMCacheBatchService cdmCacheBatchService, ConversionService conversionService, CDMCacheRepository cdmCacheRepository) { @@ -95,6 +101,19 @@ public List findAndCache(Source source, List conceptIds return cacheEntities; } + @Transactional() + public void clearCache() { + List sources = getSourceRepository().findAll(); + sources.stream().forEach(this::clearCache); + } + + @Transactional() + public void clearCache(Source source) { + if (sourceAccessor.hasAccess(source)) { + cdmCacheRepository.deleteBySource(source.getSourceId()); + } + } + private List find(Source source, List conceptIds) { if (CollectionUtils.isEmpty(conceptIds)) { return Collections.emptyList(); diff --git a/src/main/java/org/ohdsi/webapi/security/model/SourcePermissionSchema.java b/src/main/java/org/ohdsi/webapi/security/model/SourcePermissionSchema.java index c510776be8..ae457d252f 100644 --- a/src/main/java/org/ohdsi/webapi/security/model/SourcePermissionSchema.java +++ b/src/main/java/org/ohdsi/webapi/security/model/SourcePermissionSchema.java @@ -33,6 +33,7 @@ public class SourcePermissionSchema extends EntityPermissionSchema { put("cdmresults:%s:*:get", "Get Achilles reports on Source with SourceKey = %s"); put("cdmresults:%s:conceptRecordCount:post", "Get Achilles concept counts on Source with SourceKey = %s"); put("cdmresults:%s:*:*:get", "Get Achilles reports details on Source with SourceKey = %s"); + put("cdmresults:%s:clearcache:post", "Clear the Achilles and CDM results caches on Source with SourceKey = %s"); put("cohortresults:%s:*:*:get", "Get cohort results on Source with SourceKey = %s"); put("cohortresults:%s:*:*:*:get", "Get cohort results details on Source with SourceKey = %s"); put("cohortresults:%s:*:healthcareutilization:*:*:get", "Get cohort results baseline on period for Source with SourceKey = %s"); diff --git a/src/main/java/org/ohdsi/webapi/service/CDMResultsService.java b/src/main/java/org/ohdsi/webapi/service/CDMResultsService.java index 7b83d9d3bd..c887962d1c 100644 --- a/src/main/java/org/ohdsi/webapi/service/CDMResultsService.java +++ b/src/main/java/org/ohdsi/webapi/service/CDMResultsService.java @@ -44,8 +44,10 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import javax.ws.rs.Consumes; +import javax.ws.rs.ForbiddenException; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -282,7 +284,44 @@ public JobExecutionResource refreshCache(@PathParam("sourceKey") final String so } return new JobExecutionResource(); } + + /** + * Clear the cdm_cache and achilles_cache for all sources + * + * @summary Clear the cdm_cache and achilles_cache for all sources + * @return void + * @throws ForbiddenException if the user is not an admin + */ + @POST + @Path("{sourceKey}/clearCache") + @Transactional() + public void clearCacheForSource(@PathParam("sourceKey") final String sourceKey) { + if (!isSecured() || !isAdmin()) { + throw new ForbiddenException(); + } + Source source = getSourceRepository().findBySourceKey(sourceKey); + cacheService.clearCache(source); + cdmCacheService.clearCache(source); + } + /** + * Clear the cdm_cache and achilles_cache for all sources + * + * @summary Clear the cdm_cache and achilles_cache for all sources + * @return void + * @throws ForbiddenException if the user is not an admin + */ + @POST + @Path("clearCache") + @Transactional() + public void clearCache() { + if (!isSecured() || !isAdmin()) { + throw new ForbiddenException(); + } + cacheService.clearCache(); + cdmCacheService.clearCache(); + } + /** * Queries for data density report for the given sourceKey * @@ -441,9 +480,7 @@ private JobExecutionResource warmCaches(Source source) { String jobName = getWarmCacheJobName(String.valueOf(source.getSourceId()), source.getSourceKey()); List jobSteps = createCacheWarmingJobSteps(source, jobName); - SimpleJobBuilder builder = createJob(String.valueOf(source.getSourceId()), - source.getSourceKey(), - jobSteps); + SimpleJobBuilder builder = createJob(jobName, jobSteps); return runJob(source.getSourceKey(), source.getSourceId(), jobName, builder); } @@ -488,9 +525,9 @@ private void warmCaches(Collection sources) { if (counter++ >= bucketSizes[bucketIndex] - 1) { if (!allJobSteps.isEmpty()) { - SimpleJobBuilder builder = createJob(sourceIds.stream().map(String::valueOf).collect(Collectors.joining(",")), - String.join(",", sourceKeys), - allJobSteps); + String compositeJobName = getWarmCacheJobName(sourceIds.stream().map(String::valueOf) + .collect(Collectors.joining(",")), String.join(",", sourceKeys)); + SimpleJobBuilder builder = createJob(compositeJobName, allJobSteps); runJob(source.getSourceKey(), source.getSourceId(), jobName, builder); } @@ -503,9 +540,8 @@ private void warmCaches(Collection sources) { } } - private SimpleJobBuilder createJob(String sourceIds, String sourceKeys, List steps) { + private SimpleJobBuilder createJob(String jobName, List steps) { final SimpleJobBuilder[] stepBuilder = {null}; - String jobName = getWarmCacheJobName(sourceIds, sourceKeys); if (jobService.findJobByName(jobName, jobName) == null && !steps.isEmpty()) { JobBuilder jobBuilder = jobBuilders.get(jobName); @@ -576,11 +612,15 @@ private int getResultsDaimonPriority(Source source) { } private String getWarmCacheJobName(String sourceIds, String sourceKeys) { + return getJobName("warming cache", sourceIds, sourceKeys); + } + + private String getJobName(String jobType, String sourceIds, String sourceKeys) { // for multiple sources: try to compose a job name from source keys, and if it is too long - use source ids - String jobName = String.format("warming cache: %s", sourceKeys); + String jobName = String.format("%s: %s", jobType, sourceKeys); if (jobName.length() >= 100) { // job name in batch_job_instance is varchar(100) - jobName = String.format("warming cache: %s", sourceIds); + jobName = String.format("%s: %s", jobType, sourceIds); if (jobName.length() >= 100) { // if we still have more than 100 symbols jobName = jobName.substring(0, 88); diff --git a/src/main/resources/db/migration/postgresql/V2.15.0.20241113115700__add_clearcache_permission.sql b/src/main/resources/db/migration/postgresql/V2.15.0.20241113115700__add_clearcache_permission.sql new file mode 100644 index 0000000000..080e455933 --- /dev/null +++ b/src/main/resources/db/migration/postgresql/V2.15.0.20241113115700__add_clearcache_permission.sql @@ -0,0 +1,29 @@ +INSERT INTO ${ohdsiSchema}.sec_permission (id, value, description) +SELECT nextval('${ohdsiSchema}.sec_permission_id_seq'), + 'cdmresults:clearcache:post', + 'Clear the Achilles and CDM results caches'; + +INSERT INTO ${ohdsiSchema}.sec_role_permission (role_id, permission_id) +SELECT sr.id, sp.id +FROM ${ohdsiSchema}.sec_permission sp, + ${ohdsiSchema}.sec_role sr +WHERE sp."value" in + ( + 'cdmresults:clearcache:post' + ) + AND sr.name IN ('admin'); + +INSERT INTO ${ohdsiSchema}.sec_permission (id, value, description) +SELECT nextval('${ohdsiSchema}.sec_permission_id_seq'), + 'cdmresults:*:clearcache:post', + 'Clear the Achilles and CDM results caches'; + +INSERT INTO ${ohdsiSchema}.sec_role_permission (role_id, permission_id) +SELECT sr.id, sp.id +FROM ${ohdsiSchema}.sec_permission sp, + ${ohdsiSchema}.sec_role sr +WHERE sp."value" in + ( + 'cdmresults:*:clearcache:post' + ) + AND sr.name IN ('admin'); diff --git a/src/test/java/org/ohdsi/webapi/test/CDMResultsServiceIT.java b/src/test/java/org/ohdsi/webapi/test/CDMResultsServiceIT.java index 72635bad7a..51e0a356c5 100644 --- a/src/test/java/org/ohdsi/webapi/test/CDMResultsServiceIT.java +++ b/src/test/java/org/ohdsi/webapi/test/CDMResultsServiceIT.java @@ -17,6 +17,8 @@ import org.ohdsi.circe.helper.ResourceHelper; import org.ohdsi.sql.SqlRender; import org.ohdsi.sql.SqlTranslate; +import org.ohdsi.webapi.achilles.service.AchillesCacheService; +import org.ohdsi.webapi.cdmresults.service.CDMCacheService; import org.ohdsi.webapi.source.SourceRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -28,9 +30,18 @@ public class CDMResultsServiceIT extends WebApiIT { @Value("${cdmResultsService.endpoint.conceptRecordCount}") private String conceptRecordCountEndpoint; + @Value("${cdmResultsService.endpoint.clearCache}") + private String clearCacheEndpoint; + @Autowired private SourceRepository sourceRepository; + @Autowired + private AchillesCacheService achillesService; + + @Autowired + private CDMCacheService cdmCacheService; + @Before public void init() throws Exception { truncateTable(String.format("%s.%s", "public", "source")); @@ -65,7 +76,7 @@ public void requestConceptRecordCounts_firstTime_returnsResults() { final ResponseEntity>>> entity = getRestTemplate().postForEntity(this.conceptRecordCountEndpoint, conceptIds, returnClass, queryParameters ); - // Assertion + // Assert assertOK(entity); List>> results = entity.getBody(); assertEquals(1, results.size()); @@ -78,4 +89,68 @@ public void requestConceptRecordCounts_firstTime_returnsResults() { assertEquals(102, counts.get(2).intValue()); assertEquals(103, counts.get(3).intValue()); } -} + + @Test + public void achillesService_clearCache_nothingInCache_doesNothing() { + + // Arrange + + // Act + achillesService.clearCache(); + + // Assert + String sql = "SELECT COUNT(*) FROM achilles_cache"; + Integer count = jdbcTemplate.queryForObject(sql, Integer.class); + assertEquals(0, count.intValue()); + } + + @Test + public void achillesService_clearCache_somethingInCache_clearsAllRowsForSource() { + + // Arrange + String insertSqlRow1 = "INSERT INTO achilles_cache (id, source_id, cache_name, cache) VALUES (1, 1, 'cache1', 'cache1')"; + jdbcTemplate.execute(insertSqlRow1); + String insertSqlRow2 = "INSERT INTO achilles_cache (id, source_id, cache_name, cache) VALUES (2, 1, 'cache2', 'cache2')"; + jdbcTemplate.execute(insertSqlRow2); + + // Act + achillesService.clearCache(); + + // Assert + String sql = "SELECT COUNT(*) FROM achilles_cache"; + Integer count = jdbcTemplate.queryForObject(sql, Integer.class); + assertEquals(0, count.intValue()); + } + + @Test + public void cdmCacheService_clearCache_nothingInCache_doesNothing() { + + // Arrange + + // Act + cdmCacheService.clearCache(); + + // Assert + String sql = "SELECT COUNT(*) FROM cdm_cache"; + Integer count = jdbcTemplate.queryForObject(sql, Integer.class); + assertEquals(0, count.intValue()); + } + + @Test + public void cdmCacheService_clearCache_somethingInCache_clearsAllRowsForSource() { + + // Arrange + String insertSqlRow1 = "INSERT INTO cdm_cache (id, concept_id, source_id, record_count, descendant_record_count, person_count, descendant_person_count) VALUES (1, 1, 1, 100, 101, 102, 103)"; + jdbcTemplate.execute(insertSqlRow1); + String insertSqlRow2 = "INSERT INTO cdm_cache (id, concept_id, source_id, record_count, descendant_record_count, person_count, descendant_person_count) VALUES (2, 2, 1, 200, 201, 202, 203)"; + jdbcTemplate.execute(insertSqlRow2); + + // Act + cdmCacheService.clearCache(); + + // Assert + String sql = "SELECT COUNT(*) FROM cdm_cache"; + Integer count = jdbcTemplate.queryForObject(sql, Integer.class); + assertEquals(0, count.intValue()); + } + } diff --git a/src/test/java/org/ohdsi/webapi/test/ITStarter.java b/src/test/java/org/ohdsi/webapi/test/ITStarter.java index 1c12bdd850..cf7593e17f 100644 --- a/src/test/java/org/ohdsi/webapi/test/ITStarter.java +++ b/src/test/java/org/ohdsi/webapi/test/ITStarter.java @@ -21,7 +21,8 @@ SecurityIT.class, JobServiceIT.class, CohortAnalysisServiceIT.class, - VocabularyServiceIT.class + VocabularyServiceIT.class, + CDMResultsServiceIT.class }) @TestPropertySource(locations = "/application-test.properties") public class ITStarter extends AbstractShiro { diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties index 3d9a789b5b..6b79fe5e0e 100644 --- a/src/test/resources/application-test.properties +++ b/src/test/resources/application-test.properties @@ -11,8 +11,10 @@ vocabularyservice.endpoint.concept=${vocabularyservice.endpoint}/concept/1 #GET cohortdefinitions cohortdefinitionservice.endpoint.cohortdefinitions=${baseUri}/cohortdefinition -#POST cdmResults +#POST conceptRecordCount cdmResultsService.endpoint.conceptRecordCount=${cdmResultsService.endpoint}/{sourceName}/conceptRecordCount +#GET clearCache +cdmResultsService.endpoint.clearCache=${cdmResultsService.endpoint}/clearCache #Example application service exampleservice.endpoint=${baseUri}/example