diff --git a/score-server/src/main/java/bio/overture/score/server/repository/s3/S3DownloadService.java b/score-server/src/main/java/bio/overture/score/server/repository/s3/S3DownloadService.java index a2ab2bb5..85f25c48 100644 --- a/score-server/src/main/java/bio/overture/score/server/repository/s3/S3DownloadService.java +++ b/score-server/src/main/java/bio/overture/score/server/repository/s3/S3DownloadService.java @@ -17,8 +17,11 @@ */ package bio.overture.score.server.repository.s3; +import static bio.overture.score.server.metadata.MetadataService.getAnalysisId; import static com.google.common.base.Preconditions.checkArgument; +import bio.overture.score.server.metadata.MetadataEntity; +import bio.overture.score.server.metadata.MetadataService; import lombok.Cleanup; import lombok.Setter; import lombok.val; @@ -69,6 +72,7 @@ public class S3DownloadService implements DownloadService { * Constants. */ private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final String PUBLISHED_ANALYSIS_STATE = "PUBLISHED"; /** * Configuration. @@ -79,6 +83,8 @@ public class S3DownloadService implements DownloadService { private int expiration; @Value("${object.sentinel}") private String sentinelObjectId; + @Value("${metadata.useLegacyMode:false}") + private boolean useLegacyMode; /** * Dependencies. @@ -91,10 +97,14 @@ public class S3DownloadService implements DownloadService { private URLGenerator urlGenerator; @Autowired private PartCalculator partCalculator; + @Autowired + private MetadataService metadataService; @Override public ObjectSpecification download(String objectId, long offset, long length, boolean forExternalUse) { try { + checkPublishedAnalysisState(metadataService.getEntity(objectId)); + checkArgument(offset > -1L); // Retrieve our meta file for object id @@ -142,6 +152,21 @@ public ObjectSpecification download(String objectId, long offset, long length, b } } + void checkPublishedAnalysisState(MetadataEntity entity){ + if(!useLegacyMode){ + val objectId = entity.getId(); + val analysisState = metadataService.getAnalysisStateForMetadata(entity); + if (!analysisState.equals(PUBLISHED_ANALYSIS_STATE)){ + val message = String.format("Critical Error: cannot complete download for objectId '%s' with " + + "analysisState '%s' and analysisId '%s'. " + + "Can only download objects that have the analysisState '%s'. Update the file metadata and retry.", + objectId, analysisState, getAnalysisId(entity), PUBLISHED_ANALYSIS_STATE); + log.error(message); // Log to audit log file + throw new NotRetryableException(new IllegalStateException(message)); + } + } + } + // This really is a misleading method name - should be retrieveMetaFile() or something public ObjectSpecification getSpecification(String objectId) { val objectKey = ObjectKeys.getObjectKey(dataDir, objectId); diff --git a/score-server/src/main/java/bio/overture/score/server/repository/s3/S3UploadService.java b/score-server/src/main/java/bio/overture/score/server/repository/s3/S3UploadService.java index f46686ac..a3a09b6e 100644 --- a/score-server/src/main/java/bio/overture/score/server/repository/s3/S3UploadService.java +++ b/score-server/src/main/java/bio/overture/score/server/repository/s3/S3UploadService.java @@ -409,11 +409,11 @@ void checkRegistered(String objectId) { } if (!useLegacyMode){ - checkAnalysisState(entity); + checkUnpublishedAnalysisState(entity); } } - void checkAnalysisState(MetadataEntity entity){ + void checkUnpublishedAnalysisState(MetadataEntity entity){ val objectId = entity.getId(); val analysisState = metadataClient.getAnalysisStateForMetadata(entity); if (!analysisState.equals(UNPUBLISHED_ANALYSIS_STATE)){ diff --git a/score-server/src/test/java/bio/overture/score/server/repository/s3/S3DownloadServiceTest.java b/score-server/src/test/java/bio/overture/score/server/repository/s3/S3DownloadServiceTest.java new file mode 100644 index 00000000..8b50a1f3 --- /dev/null +++ b/score-server/src/test/java/bio/overture/score/server/repository/s3/S3DownloadServiceTest.java @@ -0,0 +1,61 @@ +package bio.overture.score.server.repository.s3; + +import bio.overture.score.server.exception.NotRetryableException; +import bio.overture.score.server.metadata.MetadataEntity; +import bio.overture.score.server.metadata.MetadataService; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(MockitoJUnitRunner.class) +public class S3DownloadServiceTest { + + private final String objectId = "45dfcd17-8e80-53fc-b400-cc8b583dae05"; + + private S3DownloadService s3DownloadService = new S3DownloadService(); + + private MetadataService mockService; + + private MetadataEntity metadataEntity; + + @Before + public void set_up(){ + mockService = mock(MetadataService.class); + s3DownloadService.setMetadataService(mockService); + metadataEntity = MetadataEntity.builder() + .id(objectId) + .fileName("file_1") + .access("open") + .gnosId("an1") + .projectCode("project") + .build(); + + when(mockService.getAnalysisStateForMetadata(metadataEntity)).thenReturn("UNPUBLISHED"); + } + + @Test + public void test_if_unpublished_file_triggers_error() { + val throwable = catchThrowable(() -> s3DownloadService.checkPublishedAnalysisState(metadataEntity)); + assertThat(throwable) + .hasMessageContaining( + format("Critical Error: cannot complete download for objectId '%s' with ", objectId)); + } + + @Test + public void verify_if_download_unpublished_objectId_is_blocked() { + when(mockService.getAnalysisStateForMetadata(metadataEntity)).thenReturn("UNPUBLISHED"); + when(mockService.getEntity(objectId)).thenReturn(metadataEntity); + + val throwable = catchThrowable(() -> s3DownloadService.download(objectId, 0, -1, false)); + assertThat(throwable).isExactlyInstanceOf(NotRetryableException.class); + } + +} diff --git a/score-server/src/test/java/bio/overture/score/server/service/download/ObjectDownloadServiceTest.java b/score-server/src/test/java/bio/overture/score/server/service/download/ObjectDownloadServiceTest.java index 241799ea..02905a3b 100644 --- a/score-server/src/test/java/bio/overture/score/server/service/download/ObjectDownloadServiceTest.java +++ b/score-server/src/test/java/bio/overture/score/server/service/download/ObjectDownloadServiceTest.java @@ -19,9 +19,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import java.net.URL; import java.util.regex.Pattern; @@ -30,13 +28,14 @@ import bio.overture.score.server.config.ServerConfig; import bio.overture.score.server.exception.IdNotFoundException; import bio.overture.score.core.util.SimplePartCalculator; +import bio.overture.score.server.metadata.MetadataEntity; +import bio.overture.score.server.metadata.MetadataService; import bio.overture.score.server.repository.s3.S3BucketNamingService; import bio.overture.score.server.repository.s3.S3DownloadService; import bio.overture.score.server.repository.s3.S3URLGenerator; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; @@ -69,11 +68,14 @@ public class ObjectDownloadServiceTest extends S3DownloadService { /** * SUT */ - @InjectMocks - S3DownloadService service; + S3DownloadService service = new S3DownloadService(); S3BucketNamingService namingService = new S3BucketNamingService(); + MetadataService mockService; + + MetadataEntity metadataEntity; + @Before public void setUp() { namingService.setObjectBucketName(objectBucketName); @@ -81,16 +83,28 @@ public void setUp() { namingService.setBucketPoolSize(16); namingService.setBucketKeySize(3); service.setBucketNamingService(namingService); + service.setS3Client(s3Client); - // ReflectionTestUtils.setField(service, "dataBucketName", dataBucketName); - // ReflectionTestUtils.setField(service, "stateBucketName", stateBucketName); ReflectionTestUtils.setField(service, "dataDir", dataDir); - // ReflectionTestUtils.setField(service, "bucketPoolSize", 5); - // ReflectionTestUtils.setField(service, "bucketKeySize", 2); ReflectionTestUtils.setField(service, "expiration", 7); - ReflectionTestUtils.setField(service, "urlGenerator", new S3URLGenerator()); ReflectionTestUtils.setField(service, "partCalculator", new SimplePartCalculator(20000)); + + setUpMockService(); + } + + public void setUpMockService(){ + mockService = mock(MetadataService.class); + service.setMetadataService(mockService); + metadataEntity = MetadataEntity.builder() + .id(objectId) + .fileName("file_1") + .access("open") + .gnosId("an1") + .projectCode("project") + .build(); + when(mockService.getAnalysisStateForMetadata(metadataEntity)).thenReturn("PUBLISHED"); + when(mockService.getEntity(objectId)).thenReturn(metadataEntity); } @Test(expected = IdNotFoundException.class)