From 84cb1d48866f64b3ae9ac1d5958546ea0bf414b3 Mon Sep 17 00:00:00 2001 From: Ruslan Forostianov <ruslan@se4.bio> Date: Sat, 14 Dec 2024 14:05:17 +0100 Subject: [PATCH] Simple entry point. Fetch MAF records and write them to zip file --- .../file/export/MafRecordFetcher.java | 78 ++++++++++ .../org/cbioportal/file/model/MafRecord.java | 8 +- .../java/org/cbioportal/model/Mutation.java | 143 ++++++++++++++++++ .../service/impl/ExportService.java | 116 +++++++------- .../org/cbioportal/web/ExportController.java | 42 +++++ .../persistence/mybatis/MutationMapper.xml | 29 +++- .../file/export/MafRecordWriterTest.java | 4 +- .../ExportStudyDataIntegrationTest.java | 77 ++++++++++ 8 files changed, 427 insertions(+), 70 deletions(-) create mode 100644 src/main/java/org/cbioportal/file/export/MafRecordFetcher.java create mode 100644 src/main/java/org/cbioportal/web/ExportController.java create mode 100644 src/test/java/org/cbioportal/test/integration/export/ExportStudyDataIntegrationTest.java diff --git a/src/main/java/org/cbioportal/file/export/MafRecordFetcher.java b/src/main/java/org/cbioportal/file/export/MafRecordFetcher.java new file mode 100644 index 00000000000..86dc2027c59 --- /dev/null +++ b/src/main/java/org/cbioportal/file/export/MafRecordFetcher.java @@ -0,0 +1,78 @@ +package org.cbioportal.file.export; + +import org.cbioportal.file.model.MafRecord; +import org.cbioportal.model.MolecularProfile; +import org.cbioportal.model.Mutation; +import org.cbioportal.service.MolecularProfileService; +import org.cbioportal.service.MutationService; +import org.springframework.stereotype.Component; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Component +public class MafRecordFetcher { + + private final MolecularProfileService molecularProfileService; + private final MutationService mutationService; + + public MafRecordFetcher(MolecularProfileService molecularProfileService, MutationService mutationService) { + this.molecularProfileService = molecularProfileService; + this.mutationService = mutationService; + } + + public Iterator<MafRecord> fetch(Map<String, Set<String>> sampleIdsByStudyId) { + List<String> studyIds = List.copyOf(sampleIdsByStudyId.keySet()); + List<String> molecularProfileStableIds = this.molecularProfileService.getMolecularProfilesInStudies(studyIds, "ID").stream() + .map(MolecularProfile::getStableId).toList(); + List<String> sampleIds = List.copyOf(sampleIdsByStudyId.values().stream().flatMap(Set::stream).toList()); + List<Integer> entrezGeneIds = List.of(); + List<Mutation> mutationList = mutationService.getMutationsInMultipleMolecularProfiles( + molecularProfileStableIds, sampleIds, entrezGeneIds, "EXPORT", null, null, null, null); + return mutationList.stream() + .filter(mutation -> sampleIdsByStudyId.get(mutation.getStudyId()).contains(mutation.getSampleId())) + .map(mutation -> new MafRecord( + mutation.getGene().getHugoGeneSymbol(), + mutation.getGene().getEntrezGeneId().toString(), + mutation.getCenter(), + mutation.getNcbiBuild(), + mutation.getChr(), + mutation.getStartPosition(), + mutation.getEndPosition(), + "+", + mutation.getMutationType(), + mutation.getVariantType(), + mutation.getReferenceAllele(), + mutation.getTumorSeqAllele(), + //TODO check if this is correct + mutation.getTumorSeqAllele(), + mutation.getDbSnpRs(), + mutation.getDbSnpValStatus(), + mutation.getSampleId(), + mutation.getMatchedNormSampleBarcode(), + mutation.getMatchNormSeqAllele1(), + mutation.getMatchNormSeqAllele2(), + mutation.getTumorValidationAllele1(), + mutation.getTumorValidationAllele2(), + mutation.getMatchNormValidationAllele1(), + mutation.getMatchNormValidationAllele2(), + mutation.getVerificationStatus(), + mutation.getValidationStatus(), + mutation.getMutationStatus(), + mutation.getSequencingPhase(), + mutation.getSequenceSource(), + mutation.getValidationMethod(), + mutation.getScore() == null ? null : mutation.getScore().toString(), + mutation.getBamFile(), + mutation.getSequencer(), + //TODO how to calculate HgvpShort? + "", + mutation.getTumorAltCount(), + mutation.getTumorRefCount(), + mutation.getNormalAltCount(), + mutation.getNormalRefCount() + )).iterator(); + } +} diff --git a/src/main/java/org/cbioportal/file/model/MafRecord.java b/src/main/java/org/cbioportal/file/model/MafRecord.java index 9dbda61785a..1a3bfe85825 100644 --- a/src/main/java/org/cbioportal/file/model/MafRecord.java +++ b/src/main/java/org/cbioportal/file/model/MafRecord.java @@ -32,12 +32,12 @@ public record MafRecord( /** * Start position of event. */ - Integer startPosition, + Long startPosition, /** * End position of event. */ - Integer endPosition, + Long endPosition, /** * We assume that the mutation is reported for the + strand. @@ -150,12 +150,12 @@ public record MafRecord( String validationMethod, /** - * Not used. + * Score */ String score, /** - * Not used. + * The BAM file used to call the variant. */ String bamFile, diff --git a/src/main/java/org/cbioportal/model/Mutation.java b/src/main/java/org/cbioportal/model/Mutation.java index 3ddae915587..6f5079352d5 100644 --- a/src/main/java/org/cbioportal/model/Mutation.java +++ b/src/main/java/org/cbioportal/model/Mutation.java @@ -33,6 +33,22 @@ public class Mutation extends Alteration implements Serializable { @JsonRawValue @Schema(type = "java.util.Map") private Object annotationJSON; + private String dbSnpRs; + private String dbSnpValStatus; + private String matchedNormSampleBarcode; + private String matchNormSeqAllele1; + private String matchNormSeqAllele2; + private String tumorValidationAllele1; + private String tumorValidationAllele2; + private String matchNormValidationAllele1; + private String matchNormValidationAllele2; + private String verificationStatus; + private String sequencingPhase; + private String sequenceSource; + private String validationMethod; + private BigDecimal score; + private String bamFile; + private String sequencer; public String getCenter() { return center; @@ -213,4 +229,131 @@ public void setAnnotationJSON(String annotationJSON) { this.annotationJSON = annotationJSON; } + public String getDbSnpRs() { + return dbSnpRs; + } + + public void setDbSnpRs(String dbSnpRs) { + this.dbSnpRs = dbSnpRs; + } + + public String getDbSnpValStatus() { + return dbSnpValStatus; + } + + public void setDbSnpValStatus(String dbSnpValStatus) { + this.dbSnpValStatus = dbSnpValStatus; + } + + public String getMatchedNormSampleBarcode() { + return matchedNormSampleBarcode; + } + + public void setMatchedNormSampleBarcode(String matchedNormSampleBarcode) { + this.matchedNormSampleBarcode = matchedNormSampleBarcode; + } + + public String getMatchNormSeqAllele1() { + return matchNormSeqAllele1; + } + + public void setMatchNormSeqAllele1(String matchNormSeqAllele1) { + this.matchNormSeqAllele1 = matchNormSeqAllele1; + } + + public String getMatchNormSeqAllele2() { + return matchNormSeqAllele2; + } + + public void setMatchNormSeqAllele2(String matchNormSeqAllele2) { + this.matchNormSeqAllele2 = matchNormSeqAllele2; + } + + public String getTumorValidationAllele1() { + return tumorValidationAllele1; + } + + public void setTumorValidationAllele1(String tumorValidationAllele1) { + this.tumorValidationAllele1 = tumorValidationAllele1; + } + + public String getTumorValidationAllele2() { + return tumorValidationAllele2; + } + + public void setTumorValidationAllele2(String tumorValidationAllele2) { + this.tumorValidationAllele2 = tumorValidationAllele2; + } + + public String getMatchNormValidationAllele1() { + return matchNormValidationAllele1; + } + + public void setMatchNormValidationAllele1(String matchNormValidationAllele1) { + this.matchNormValidationAllele1 = matchNormValidationAllele1; + } + + public String getMatchNormValidationAllele2() { + return matchNormValidationAllele2; + } + + public void setMatchNormValidationAllele2(String matchNormValidationAllele2) { + this.matchNormValidationAllele2 = matchNormValidationAllele2; + } + + public String getVerificationStatus() { + return verificationStatus; + } + + public void setVerificationStatus(String verificationStatus) { + this.verificationStatus = verificationStatus; + } + + public String getSequencingPhase() { + return sequencingPhase; + } + + public void setSequencingPhase(String sequencingPhase) { + this.sequencingPhase = sequencingPhase; + } + + public String getSequenceSource() { + return sequenceSource; + } + + public void setSequenceSource(String sequenceSource) { + this.sequenceSource = sequenceSource; + } + + public String getValidationMethod() { + return validationMethod; + } + + public void setValidationMethod(String validationMethod) { + this.validationMethod = validationMethod; + } + + public BigDecimal getScore() { + return score; + } + + public void setScore(BigDecimal score) { + this.score = score; + } + + public String getBamFile() { + return bamFile; + } + + public void setBamFile(String bamFile) { + this.bamFile = bamFile; + } + + public String getSequencer() { + return sequencer; + } + + public void setSequencer(String sequencer) { + this.sequencer = sequencer; + } } diff --git a/src/main/java/org/cbioportal/service/impl/ExportService.java b/src/main/java/org/cbioportal/service/impl/ExportService.java index f72c98bdbd4..c1cac6120d2 100644 --- a/src/main/java/org/cbioportal/service/impl/ExportService.java +++ b/src/main/java/org/cbioportal/service/impl/ExportService.java @@ -1,76 +1,74 @@ package org.cbioportal.service.impl; -import org.apache.commons.lang3.tuple.Pair; -import org.cbioportal.model.*; +import org.cbioportal.file.export.MafRecordFetcher; +import org.cbioportal.file.export.MafRecordWriter; +import org.cbioportal.model.CancerStudy; +import org.cbioportal.model.Sample; import org.cbioportal.service.*; -import org.cbioportal.service.exception.MolecularProfileNotFoundException; -import org.cbioportal.service.exception.PatientNotFoundException; -import org.cbioportal.service.exception.SampleNotFoundException; -import org.cbioportal.service.exception.StudyNotFoundException; -import org.springframework.beans.factory.annotation.Autowired; +import org.cbioportal.service.util.SessionServiceRequestHandler; +import org.cbioportal.web.parameter.VirtualStudy; +import org.cbioportal.web.parameter.VirtualStudySamples; import org.springframework.stereotype.Service; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; @Service public class ExportService { - @Autowired - PatientService patientService; - - @Autowired - SampleService sampleService; - - @Autowired - ClinicalDataService clinicalDataService; - - @Autowired - MutationService mutationService; - - @Autowired - MolecularProfileService molecularProfileService; + private final StudyService studyService; + private final SessionServiceRequestHandler sessionServiceRequestHandler; + private final SampleService sampleService; + private final MafRecordFetcher mafRecordFetcher; + + public ExportService(StudyService studyService, + SessionServiceRequestHandler sessionServiceRequestHandler, + SampleService sampleService, + MafRecordFetcher mafRecordFetcher) { + this.studyService = studyService; + this.sessionServiceRequestHandler = sessionServiceRequestHandler; + this.sampleService = sampleService; + this.mafRecordFetcher = mafRecordFetcher; + } - public void exportData(Map<String, List<String>> samples) throws SampleNotFoundException, StudyNotFoundException, PatientNotFoundException, MolecularProfileNotFoundException { - if (samples.size() > 1) { - //virtual study + public ByteArrayOutputStream exportStudyDataToZip(String studyId) throws IOException { + List<CancerStudy> studies = studyService.fetchStudies(List.of(studyId), "DETAILED"); + Map<String, Set<String>> studyToSampleMap = new HashMap<>(); + if (studies.isEmpty()) { + VirtualStudy virtualStudy = sessionServiceRequestHandler.getVirtualStudyById(studyId); + studyToSampleMap.putAll( + virtualStudy.getData().getStudies().stream().collect(Collectors.toMap(VirtualStudySamples::getId, VirtualStudySamples::getSamples))); + } else { + List<Sample> samples = sampleService.getAllSamplesInStudies(List.of(studyId), "ID", null, null, null, null); + studyToSampleMap.put(studyId, samples.stream().map(Sample::getStableId).collect(Collectors.toSet())); } - for (Map.Entry<String, List<String>> studySamples: samples.entrySet()) { - String studyId = studySamples.getKey(); - for (String sampleId: studySamples.getValue()) { - Sample sample = sampleService.getSampleInStudy(studyId, sampleId); - String patientStableId = sample.getPatientStableId(); - String sampleStableId = sample.getStableId(); - List<ClinicalData> sampleClinicalData = clinicalDataService.getAllClinicalDataOfSampleInStudy(studyId, sampleStableId, null, null, null, null, null, null); - for (ClinicalData sampleClinicalDatum : sampleClinicalData) { - ClinicalAttribute clinicalAttribute = sampleClinicalDatum.getClinicalAttribute(); - sampleClinicalDatum.getAttrId(); - sampleClinicalDatum.getAttrValue(); - } - Patient patient = patientService.getPatientInStudy(studyId, patientStableId); - List<ClinicalData> patientClinicalData = clinicalDataService.getAllClinicalDataOfPatientInStudy(studyId, patientStableId, null, null, null, null, null, null); - for (ClinicalData patientClinicalDataItem : patientClinicalData) { - ClinicalAttribute clinicalAttribute = patientClinicalDataItem.getClinicalAttribute(); - patientClinicalDataItem.getAttrId(); - patientClinicalDataItem.getAttrValue(); - } - } - List<MolecularProfileCaseIdentifier> molecularProfileCaseIdentifiers = molecularProfileService.getMolecularProfileCaseIdentifiers(List.of(studyId), studySamples.getValue()); - for (MolecularProfileCaseIdentifier molecularProfileCaseIdentifier : molecularProfileCaseIdentifiers) { - MolecularProfile molecularProfile = molecularProfileService.getMolecularProfile(molecularProfileCaseIdentifier.getMolecularProfileId()); - MolecularProfile.MolecularAlterationType molecularAlterationType = molecularProfile.getMolecularAlterationType(); - molecularProfile.getDatatype(); - molecularProfile.getName(); - switch (molecularAlterationType) { - case MUTATION_EXTENDED -> { - List<Mutation> mutationList = mutationService.getMutationsInMultipleMolecularProfilesByGeneQueries(List.of(molecularProfileCaseIdentifier.getMolecularProfileId()), studySamples.getValue(), List.of(), "DETAILED", 10000, 1, null, null); - for (Mutation mutation : mutationList) { - mutation.getChr(); - } - } - } - } + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try (ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { + // Add files to the ZIP + StringWriter mafRecordsStringWriter = new StringWriter(); + MafRecordWriter mafRecordWriter = new MafRecordWriter(mafRecordsStringWriter); + //TODO do not produce the file if no data has been retrieved + mafRecordWriter.write(mafRecordFetcher.fetch(studyToSampleMap)); + addFileToZip(zipOutputStream, "data_mutation.txt", mafRecordsStringWriter.toString().getBytes()); } + return byteArrayOutputStream; + } + + private void addFileToZip(ZipOutputStream zipOutputStream, String fileName, byte[] fileContent) throws IOException { + // Create a new ZIP entry for the file + ZipEntry zipEntry = new ZipEntry(fileName); + zipOutputStream.putNextEntry(zipEntry); + + // Write file content + zipOutputStream.write(fileContent); + zipOutputStream.closeEntry(); } } diff --git a/src/main/java/org/cbioportal/web/ExportController.java b/src/main/java/org/cbioportal/web/ExportController.java new file mode 100644 index 00000000000..73735b5e2cd --- /dev/null +++ b/src/main/java/org/cbioportal/web/ExportController.java @@ -0,0 +1,42 @@ +package org.cbioportal.web; + +import org.cbioportal.service.impl.ExportService; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +@RestController +public class ExportController { + + private final ExportService exportService; + + public ExportController(ExportService exportService) { + this.exportService = exportService; + } + + //TODO make it work for virtual studies as well + //@PreAuthorize("hasPermission(#studyId, 'CancerStudyId', T(org.cbioportal.utils.security.AccessLevel).READ)") + @GetMapping("/export/study/{studyId}.zip") + public ResponseEntity<byte[]> downloadStudyData(@PathVariable String studyId) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = exportService.exportStudyDataToZip(studyId); + + // Build the response + byte[] zipBytes = byteArrayOutputStream.toByteArray(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(new MediaType("application", "zip")); + headers.setContentDispositionFormData("attachment", studyId + ".zip"); + + return ResponseEntity.ok() + .headers(headers) + .body(zipBytes); + } +} diff --git a/src/main/resources/org/cbioportal/persistence/mybatis/MutationMapper.xml b/src/main/resources/org/cbioportal/persistence/mybatis/MutationMapper.xml index de6a18dc104..2219580b315 100644 --- a/src/main/resources/org/cbioportal/persistence/mybatis/MutationMapper.xml +++ b/src/main/resources/org/cbioportal/persistence/mybatis/MutationMapper.xml @@ -9,7 +9,7 @@ patient.STABLE_ID AS "patientId", mutation.ENTREZ_GENE_ID AS "entrezGeneId", cancer_study.CANCER_STUDY_IDENTIFIER AS "studyId" - <if test="projection == 'SUMMARY' || projection == 'DETAILED'"> + <if test="projection == 'SUMMARY' || projection == 'DETAILED' || projection == 'EXPORT'"> , mutation.CENTER AS "center", mutation.MUTATION_STATUS AS "mutationStatus", @@ -38,7 +38,7 @@ alteration_driver_annotation.DRIVER_TIERS_FILTER AS "driverTiersFilter", alteration_driver_annotation.DRIVER_TIERS_FILTER_ANNOTATION as "driverTiersFilterAnnotation" </if> - <if test="projection == 'DETAILED'"> + <if test="projection == 'DETAILED' || projection == 'EXPORT'"> , <include refid="org.cbioportal.persistence.mybatis.GeneMapper.select"> <property name="prefix" value="gene."/> @@ -48,6 +48,25 @@ <property name="prefix" value="alleleSpecificCopyNumber."/> </include> </if> + <if test="projection == 'EXPORT'"> + , + mutation.MATCHED_NORM_SAMPLE_BARCODE as "matchedNormSampleBarcode", + mutation.MATCH_NORM_SEQ_ALLELE1 as "matchNormSeqAllele1", + mutation.MATCH_NORM_SEQ_ALLELE2 as "matchNormSeqAllele2", + mutation.TUMOR_VALIDATION_ALLELE1 as "tumorValidationAllele1", + mutation.TUMOR_VALIDATION_ALLELE2 as "tumorValidationAllele2", + mutation.MATCH_NORM_VALIDATION_ALLELE1 as "matchNormValidationAllele1", + mutation.MATCH_NORM_VALIDATION_ALLELE2 as "matchNormValidationAllele2", + mutation.VERIFICATION_STATUS as "verificationStatus", + mutation.SEQUENCING_PHASE as "sequencingPhase", + mutation.SEQUENCE_SOURCE as "sequenceSource", + mutation.VALIDATION_METHOD as "validationMethod", + mutation.SCORE as "score", + mutation.BAM_FILE as "bamFile", + mutation.SEQUENCER as "sequencer", + mutation_event.DB_SNP_RS AS "dbSnpRs", + mutation_event.DB_SNP_VAL_STATUS AS "dbSnpValStatus" + </if> </sql> <sql id="projectionAndLimitFilter"> @@ -294,7 +313,7 @@ <include refid="select"/> <include refid="from"/> INNER JOIN mutation_event ON mutation.MUTATION_EVENT_ID = mutation_event.MUTATION_EVENT_ID - <if test="projection == 'DETAILED'"> + <if test="projection == 'DETAILED' || projection == 'EXPORT'"> INNER JOIN gene ON mutation.ENTREZ_GENE_ID = gene.ENTREZ_GENE_ID <include refid="includeAlleleSpecificCopyNumber"/> </if> @@ -324,7 +343,7 @@ <include refid="select"/> <include refid="from"/> INNER JOIN mutation_event ON mutation.MUTATION_EVENT_ID = mutation_event.MUTATION_EVENT_ID - <if test="projection == 'DETAILED'"> + <if test="projection == 'DETAILED' || projection == 'EXPORT'"> INNER JOIN gene ON mutation.ENTREZ_GENE_ID = gene.ENTREZ_GENE_ID <include refid="includeAlleleSpecificCopyNumber"/> </if> @@ -337,7 +356,7 @@ <include refid="select"/> <include refid="from"/> INNER JOIN mutation_event ON mutation.MUTATION_EVENT_ID = mutation_event.MUTATION_EVENT_ID - <if test="projection == 'DETAILED'"> + <if test="projection == 'DETAILED' || projection == 'EXPORT'"> INNER JOIN gene ON mutation.ENTREZ_GENE_ID = gene.ENTREZ_GENE_ID <include refid="includeAlleleSpecificCopyNumber"/> </if> diff --git a/src/test/java/org/cbioportal/file/export/MafRecordWriterTest.java b/src/test/java/org/cbioportal/file/export/MafRecordWriterTest.java index 990f1efec33..cdc2ddfa882 100644 --- a/src/test/java/org/cbioportal/file/export/MafRecordWriterTest.java +++ b/src/test/java/org/cbioportal/file/export/MafRecordWriterTest.java @@ -22,8 +22,8 @@ public void testMafRecordWriter() { "center1", "hg38", "X", - 1000000, - 1000100, + 1000000L, + 1000100L, "+", "Missense_Mutation", "SNP", diff --git a/src/test/java/org/cbioportal/test/integration/export/ExportStudyDataIntegrationTest.java b/src/test/java/org/cbioportal/test/integration/export/ExportStudyDataIntegrationTest.java new file mode 100644 index 00000000000..978fad32829 --- /dev/null +++ b/src/test/java/org/cbioportal/test/integration/export/ExportStudyDataIntegrationTest.java @@ -0,0 +1,77 @@ +package org.cbioportal.test.integration.export; + +import org.cbioportal.test.integration.security.ContainerConfig; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.core.io.Resource; +import org.springframework.http.*; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.cbioportal.test.integration.security.ContainerConfig.*; + +@RunWith(SpringRunner.class) +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT +) +@TestPropertySource( + properties = { + "authenticate=false", + "session.endpoint.publisher-api-key=this-is-a-secret", + "session.service.url=http://localhost:" + SESSION_SERVICE_PORT + "/api/sessions/public_portal/", + // DB settings (also see MysqlInitializer) + "spring.datasource.driverClassName=com.mysql.jdbc.Driver", + "spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect", + } +) +@ContextConfiguration(initializers = { + MyMysqlInitializer.class, + PortInitializer.class +}) +@DirtiesContext +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class ExportStudyDataIntegrationTest extends ContainerConfig { + + static final String CBIO_URL = String.format("http://localhost:%d", CBIO_PORT); + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void test1NoPublicVirtualStudiesAtTheBeginning() throws IOException { + ResponseEntity<Resource> response1 = restTemplate.getForEntity( + CBIO_URL + "/export/study/study_tcga_pub.zip", Resource.class); + + assertThat(response1.getStatusCode().is2xxSuccessful()).isTrue(); + HttpHeaders headers = response1.getHeaders(); + assertThat(headers.getContentType()).isNotNull(); + assertThat(headers.getContentType().toString()).isEqualTo("application/zip"); + + // Verify Content-Disposition header for file name + String contentDisposition = headers.getFirst(HttpHeaders.CONTENT_DISPOSITION); + assertThat(contentDisposition).isNotNull(); + assertThat(contentDisposition).contains("attachment"); + assertThat(contentDisposition).contains("study_tcga_pub.zip"); + // Ensure the ZIP file is not empty + try (InputStream zipInputStream = response1.getBody().getInputStream(); + ZipInputStream zis = new ZipInputStream(zipInputStream)) { + + // Ensure there's at least one entry in the ZIP + ZipEntry entry = zis.getNextEntry(); + assertThat(entry).isNotNull(); // Assert that the ZIP contains at least one file + } + } +} \ No newline at end of file