From adb91f4529eb7ac0a86b935f9122ad75c7467b2c Mon Sep 17 00:00:00 2001 From: Jhoan Munoz Date: Fri, 28 Jul 2023 18:09:58 +0100 Subject: [PATCH 1/7] Pivotal ID # 184977702: Assign DOIs Automatically - Validate the required DOI fields - Perform the request to register the DOI for the submission --- .../ebi/ac/uk/model/constants/Fields.kt | 1 + .../properties/ApplicationProperties.kt | 8 + .../ebi/biostd/itest/itest/ITestListener.kt | 8 + .../biostd/common/config/SubmitterConfig.kt | 7 +- .../src/main/resources/application-local.yml | 5 + .../src/main/resources/application.yml | 5 + submission/submitter/build.gradle.kts | 2 + .../submission/exceptions/DoiExceptions.kt | 20 ++ .../ebi/biostd/submission/model/DoiRequest.kt | 80 ++++++++ .../biostd/submission/service/DoiService.kt | 102 ++++++++++ .../submitter/SubmissionSubmitter.kt | 7 + .../submission/service/DoiServiceTest.kt | 174 ++++++++++++++++++ .../submitter/SubmissionSubmitterTest.kt | 46 +++++ .../src/test/resources/ExpectedDOIRequest.xml | 37 ++++ 14 files changed, 501 insertions(+), 1 deletion(-) create mode 100644 submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt create mode 100644 submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt create mode 100644 submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt create mode 100644 submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt create mode 100644 submission/submitter/src/test/resources/ExpectedDOIRequest.xml diff --git a/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/constants/Fields.kt b/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/constants/Fields.kt index 37cc5fc6a2..e6cd44d0b0 100644 --- a/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/constants/Fields.kt +++ b/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/constants/Fields.kt @@ -46,6 +46,7 @@ enum class SubFields(override val value: String) : Fields { TITLE("Title"), ROOT_PATH("RootPath"), PUBLIC_ACCESS_TAG("Public"), + DOI_REQUESTED("RequestDOI"), RELEASE_DATE("ReleaseDate"), RELEASE_TIME("rtime"), diff --git a/submission/submission-config/src/main/kotlin/ac/uk/ebi/biostd/common/properties/ApplicationProperties.kt b/submission/submission-config/src/main/kotlin/ac/uk/ebi/biostd/common/properties/ApplicationProperties.kt index 8567e6fc97..6ae8ff3618 100644 --- a/submission/submission-config/src/main/kotlin/ac/uk/ebi/biostd/common/properties/ApplicationProperties.kt +++ b/submission/submission-config/src/main/kotlin/ac/uk/ebi/biostd/common/properties/ApplicationProperties.kt @@ -20,6 +20,7 @@ data class ApplicationProperties( val validator: ValidatorProperties, val persistence: PersistenceProperties, val notifications: NotificationsProperties, + val doi: DoiProperties, ) data class RetryProperties( @@ -58,3 +59,10 @@ data class NotificationsProperties( val requestQueue: String, val requestRoutingKey: String, ) + +data class DoiProperties( + val endpoint: String, + val uiUrl: String, + val user: String, + val password: String +) diff --git a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/itest/ITestListener.kt b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/itest/ITestListener.kt index 9577a1e32b..f64803081f 100644 --- a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/itest/ITestListener.kt +++ b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/itest/ITestListener.kt @@ -36,6 +36,7 @@ class ITestListener : TestExecutionListener { fireSetup() ftpSetup() appPropertiesSetup() + doiSetup() } override fun testPlanExecutionFinished(testPlan: TestPlan) { @@ -97,6 +98,13 @@ class ITestListener : TestExecutionListener { System.setProperty("app.persistence.enableFire", "${System.getProperty("enableFire").toBoolean()}") } + private fun doiSetup() { + System.setProperty("app.doi.endpoint", "https://test.crossref.org/servlet/deposit") + System.setProperty("app.doi.uiUrl", "https://www.ebi.ac.uk/biostudies/") + System.setProperty("app.doi.user", "a-user") + System.setProperty("app.doi.password", "a-password") + } + companion object { private val testAppFolder = Files.createTempDirectory("test-app-folder").toFile() private const val defaultBucket = "bio-fire-bucket" diff --git a/submission/submission-webapp/src/main/kotlin/ac/uk/ebi/biostd/common/config/SubmitterConfig.kt b/submission/submission-webapp/src/main/kotlin/ac/uk/ebi/biostd/common/config/SubmitterConfig.kt index 139742cb79..359de87c6e 100644 --- a/submission/submission-webapp/src/main/kotlin/ac/uk/ebi/biostd/common/config/SubmitterConfig.kt +++ b/submission/submission-webapp/src/main/kotlin/ac/uk/ebi/biostd/common/config/SubmitterConfig.kt @@ -16,6 +16,7 @@ import ac.uk.ebi.biostd.persistence.filesystem.api.FileStorageService import ac.uk.ebi.biostd.persistence.filesystem.pagetab.PageTabService import ac.uk.ebi.biostd.submission.service.AccNoService import ac.uk.ebi.biostd.submission.service.CollectionProcessor +import ac.uk.ebi.biostd.submission.service.DoiService import ac.uk.ebi.biostd.submission.service.FileSourcesService import ac.uk.ebi.biostd.submission.service.TimesService import ac.uk.ebi.biostd.submission.submitter.ExtSubmissionSubmitter @@ -172,11 +173,13 @@ class SubmitterConfig( @Bean fun submissionSubmitter( + doiService: DoiService, extSubmissionSubmitter: ExtSubmissionSubmitter, submissionProcessor: SubmissionProcessor, collectionValidationService: CollectionValidationService, draftService: SubmissionDraftPersistenceService, ): SubmissionSubmitter = SubmissionSubmitter( + doiService, extSubmissionSubmitter, submissionProcessor, collectionValidationService, @@ -227,7 +230,6 @@ class SubmitterConfig( } @Configuration - @Suppress("MagicNumber") @EnableConfigurationProperties class ServiceConfig( private val service: PersistenceService, @@ -238,6 +240,9 @@ class SubmitterConfig( @Bean fun accNoPatternUtil() = AccNoPatternUtil() + @Bean + fun doiService(webClient: WebClient) = DoiService(webClient, properties.doi) + @Bean fun accNoService() = AccNoService(service, accNoPatternUtil(), userPrivilegesService, properties.subBasePath) diff --git a/submission/submission-webapp/src/main/resources/application-local.yml b/submission/submission-webapp/src/main/resources/application-local.yml index 6324665db3..6efd73cd76 100644 --- a/submission/submission-webapp/src/main/resources/application-local.yml +++ b/submission/submission-webapp/src/main/resources/application-local.yml @@ -54,3 +54,8 @@ app: magicDirPath: # Absolute path to the folder to be used for the user/groups magic folders links environment: LOCAL requireActivation: false + doi: + endpoint: https://test-endpoint.org + uiUrl: https://www.biostudies.ac.uk + user: a-user + password: a-password diff --git a/submission/submission-webapp/src/main/resources/application.yml b/submission/submission-webapp/src/main/resources/application.yml index 3ffcbedeea..04220e7097 100644 --- a/submission/submission-webapp/src/main/resources/application.yml +++ b/submission/submission-webapp/src/main/resources/application.yml @@ -110,3 +110,8 @@ app: notifications: requestQueue: submission-request-submitter-queue # queue used to handle request stages requestRoutingKey: bio.submission.requested # request messages routing key + doi: + endpoint: # DOI registration endpoint + uiUrl: # The BioStudies UI URL which will be associated to the DOI record + user: # User for the DOI service + password: # Password for the DOI service diff --git a/submission/submitter/build.gradle.kts b/submission/submitter/build.gradle.kts index d7a4c72aca..f43d04afee 100644 --- a/submission/submitter/build.gradle.kts +++ b/submission/submitter/build.gradle.kts @@ -19,6 +19,7 @@ import SpringBootDependencies.SpringBootStarterDataJpa import SpringBootDependencies.SpringBootStarterWeb import TestDependencies.BaseTestCompileDependencies import TestDependencies.BaseTestRuntimeDependencies +import TestDependencies.KotlinXmlBuilder import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension import org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES @@ -50,6 +51,7 @@ dependencies { implementation(KotlinReflect) implementation(KotlinStdLib) implementation(KotlinLogging) + implementation(KotlinXmlBuilder) implementation(SpringBootStarterDataJpa) implementation(SpringBootStarterWeb) diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt new file mode 100644 index 0000000000..cce0ac150f --- /dev/null +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt @@ -0,0 +1,20 @@ +package ac.uk.ebi.biostd.submission.exceptions + +class MissingDoiFieldException(field: String) : RuntimeException("The required DOI field '$field' could not be found") + +class MissingTitleException : RuntimeException("A title is required for DOI registration") + +class InvalidOrgNamesException( + organizations: List +) : RuntimeException("The following organization names are empty: ${organizations.joinToString(", ")}") + +class InvalidOrgException : RuntimeException("Organizations are required to have an accession") + +class InvalidAuthorNameException : RuntimeException("Authors are required to have a name") + +class MissingAuthorAffiliationException : RuntimeException("Authors are required to have an affiliation") + +class InvalidAuthorAffiliationException( + author: String, + organization: String, +) : RuntimeException("The organization '$organization' affiliated to the author '$author' could not be found") diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt new file mode 100644 index 0000000000..b5b1b43b0a --- /dev/null +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt @@ -0,0 +1,80 @@ +package ac.uk.ebi.biostd.submission.model + +import ebi.ac.uk.util.collections.ifNotEmpty +import org.redundent.kotlin.xml.xml +import java.time.Instant + +internal data class Contributor( + val name: String, + val surname: String, + val affiliation: String, + val orcid: String? +) + +internal class DoiRequest( + private val accNo: String, + private val title: String, + private val instanceUrl: String, + private val contributors: List, +) { + fun asXmlRequest(): String { + val timestamp = Instant.now().epochSecond.toString() + return xml("doi_batch") { + xmlns = "http://www.crossref.org/schema/4.4.1" + "head" { + "doi_batch_id" { -timestamp } + "timestamp" { -timestamp } + "depositor" { + "depositor_name" { -DEPOSITOR } + "email_address" { -EMAIL } + } + "registrant" { -DEPOSITOR } + } + "body" { + "database" { + "database_metadata" { + attribute("language", "en") + "titles" { + "title" { -BS_TITLE } + } + } + "dataset" { + contributors.ifNotEmpty { + "contributors" { + contributors.forEachIndexed { index, contributor -> + "person_name" { + attribute("contributor_role", "author") + attribute("sequence", index) + "given_name" { -contributor.name } + "surname" { -contributor.surname } + "affiliation" { -contributor.affiliation } + contributor.orcid?.let { orcid -> + "ORCID" { + attribute("authenticated", "false") + -orcid + } + } + } + } + } + } + "titles" { + "title" { -title } + } + "doi_data" { + "doi" { -"$BS_DOI_ID/$accNo" } + "resource" { -"$instanceUrl/studies/$accNo" } + } + } + } + } + }.toString() + } + + companion object { + const val BS_DOI_ID = "10.6019" + const val BS_TITLE = "BioStudies Database" + const val DEPOSITOR = "EMBL-EBI" + const val EMAIL = "biostudies@ebi.ac.uk" + } +} diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt new file mode 100644 index 0000000000..0dde6a9dbf --- /dev/null +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt @@ -0,0 +1,102 @@ +package ac.uk.ebi.biostd.submission.service + +import ac.uk.ebi.biostd.common.properties.DoiProperties +import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorAffiliationException +import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorNameException +import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgException +import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgNamesException +import ac.uk.ebi.biostd.submission.exceptions.MissingAuthorAffiliationException +import ac.uk.ebi.biostd.submission.exceptions.MissingDoiFieldException +import ac.uk.ebi.biostd.submission.exceptions.MissingTitleException +import ac.uk.ebi.biostd.submission.model.Contributor +import ac.uk.ebi.biostd.submission.model.DoiRequest +import ebi.ac.uk.commons.http.ext.RequestParams +import ebi.ac.uk.commons.http.ext.post +import ebi.ac.uk.extended.model.ExtSection +import ebi.ac.uk.extended.model.ExtSubmission +import ebi.ac.uk.extended.model.allSections +import ebi.ac.uk.extended.model.computedTitle +import ebi.ac.uk.io.FileUtils +import ebi.ac.uk.util.collections.ifNotEmpty +import mu.KotlinLogging +import org.springframework.core.io.FileSystemResource +import org.springframework.util.LinkedMultiValueMap +import org.springframework.web.reactive.function.client.WebClient +import java.nio.file.Files + +internal const val AFFILIATION_ATTR = "affiliation" +internal const val NAME_ATTR = "name" +internal const val ORCID_ATTR = "orcid" + +internal const val ORG_TYPE = "organization" +internal const val AUTHOR_TYPE = "author" + +internal const val FILE_PARAM = "fname" +internal const val OPERATION_PARAM = "operation" +internal const val OPERATION_PARAM_VALUE = "doMDUpload" +internal const val PASSWORD_PARAM = "login_password" +internal const val USER_PARAM = "login_id" +internal const val TEMP_FILE_NAME = "doi-request" + +private val logger = KotlinLogging.logger {} + +@Suppress("ThrowsCount") +class DoiService( + private val webClient: WebClient, + private val properties: DoiProperties, +) { + fun registerDoi(sub: ExtSubmission) { + val title = requireNotNull(sub.computedTitle) { throw MissingTitleException() } + val request = DoiRequest(sub.accNo, title, properties.uiUrl, getContributors(sub)) + val requestFile = Files.createTempFile("${TEMP_FILE_NAME}_${sub.accNo}", ".xml").toFile() + FileUtils.writeContent(requestFile, request.asXmlRequest()) + + val body = LinkedMultiValueMap().apply { + add(USER_PARAM, properties.user) + add(PASSWORD_PARAM, properties.password) + add(OPERATION_PARAM, OPERATION_PARAM_VALUE) + add(FILE_PARAM, FileSystemResource(requestFile)) + } + + logger.info { "${sub.accNo} ${sub.owner} Registering DOI" } + webClient.post(properties.endpoint, RequestParams(body = body)) + } + + private fun getContributors(sub: ExtSubmission): List { + val organizations = getOrganizations(sub) + return sub.allSections + .filter { it.type.lowercase() == AUTHOR_TYPE } + .ifEmpty { throw MissingDoiFieldException(AUTHOR_TYPE) } + .map { it.asContributor(organizations) } + } + + private fun ExtSection.asContributor(orgs: Map): Contributor { + val attrsMap = attributes.associateBy({ it.name.lowercase() }, { it.value }) + val names = requireNotNull(attrsMap[NAME_ATTR]) { throw InvalidAuthorNameException() } + val affiliation = requireNotNull(attrsMap[AFFILIATION_ATTR]) { throw MissingAuthorAffiliationException() } + val org = requireNotNull(orgs[affiliation]) { throw InvalidAuthorAffiliationException(names, affiliation) } + + return Contributor( + name = names.substringBeforeLast(" ", ""), + surname = names.substringAfterLast(" "), + affiliation = org, + orcid = attrsMap[ORCID_ATTR] + ) + } + + private fun getOrganizations(sub: ExtSubmission): Map { + val organizations = sub.allSections + .filter { it.type.lowercase() == ORG_TYPE } + .ifEmpty { throw MissingDoiFieldException(ORG_TYPE) } + .associateBy( + { requireNotNull(it.accNo) { throw InvalidOrgException() } }, + { org -> org.attributes.find { it.name.lowercase() == NAME_ATTR }?.value.orEmpty() }, + ) + + organizations.entries + .filter { it.value.isEmpty() } + .ifNotEmpty { entries -> throw InvalidOrgNamesException(entries.map { it.key }) } + + return organizations + } +} diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt index cb5173def6..08377d0966 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt @@ -4,6 +4,7 @@ import ac.uk.ebi.biostd.persistence.common.request.ExtSubmitRequest import ac.uk.ebi.biostd.persistence.common.service.SubmissionDraftPersistenceService import ac.uk.ebi.biostd.submission.exceptions.InvalidSubmissionException import ac.uk.ebi.biostd.submission.model.SubmitRequest +import ac.uk.ebi.biostd.submission.service.DoiService import ac.uk.ebi.biostd.submission.validator.collection.CollectionValidationService import ebi.ac.uk.extended.events.RequestCheckedReleased import ebi.ac.uk.extended.events.RequestCleaned @@ -13,12 +14,14 @@ import ebi.ac.uk.extended.events.RequestIndexed import ebi.ac.uk.extended.events.RequestLoaded import ebi.ac.uk.extended.events.RequestPersisted import ebi.ac.uk.extended.model.ExtSubmission +import ebi.ac.uk.model.constants.SubFields.DOI_REQUESTED import mu.KotlinLogging private val logger = KotlinLogging.logger {} @Suppress("TooManyFunctions") class SubmissionSubmitter( + private val doiService: DoiService, private val submissionSubmitter: ExtSubmissionSubmitter, private val submissionProcessor: SubmissionProcessor, private val collectionValidationService: CollectionValidationService, @@ -74,6 +77,7 @@ class SubmissionSubmitter( rqt.draftKey?.let { startProcessingDraft(rqt.accNo, rqt.owner, it) } val processed = submissionProcessor.processSubmission(rqt) collectionValidationService.executeCollectionValidators(processed) + if (processed.requiresDoi) doiService.registerDoi(processed) rqt.draftKey?.let { acceptDraft(rqt.accNo, rqt.owner, it) } logger.info { "${rqt.accNo} ${rqt.owner} Finished processing submission request" } @@ -99,4 +103,7 @@ class SubmissionSubmitter( draftService.setActiveStatus(draftKey) logger.info { "$accNo $owner Status of draft with key '$draftKey' set to ACTIVE" } } + + private val ExtSubmission.requiresDoi: Boolean + get() = attributes.find { it.name == DOI_REQUESTED.value } != null } diff --git a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt new file mode 100644 index 0000000000..d2dca971c9 --- /dev/null +++ b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt @@ -0,0 +1,174 @@ +package ac.uk.ebi.biostd.submission.service + +import ac.uk.ebi.biostd.common.properties.DoiProperties +import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorAffiliationException +import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorNameException +import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgException +import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgNamesException +import ac.uk.ebi.biostd.submission.exceptions.MissingAuthorAffiliationException +import ac.uk.ebi.biostd.submission.exceptions.MissingDoiFieldException +import ac.uk.ebi.biostd.submission.exceptions.MissingTitleException +import arrow.core.Either.Companion.left +import ebi.ac.uk.extended.model.ExtAttribute +import ebi.ac.uk.extended.model.ExtSection +import ebi.ac.uk.test.basicExtSubmission +import io.mockk.clearAllMocks +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.junit5.MockKExtension +import io.mockk.mockkStatic +import io.mockk.slot +import io.mockk.verify +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.core.io.FileSystemResource +import org.springframework.util.LinkedMultiValueMap +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec +import java.nio.file.Files +import java.nio.file.Paths +import java.time.Instant +import java.time.OffsetDateTime +import java.time.ZoneOffset.UTC + +@ExtendWith(MockKExtension::class) +class DoiServiceTest( + @MockK private val webClient: WebClient, +) { + private val testInstance = DoiService(webClient, properties) + private val mockNow = OffsetDateTime.of(2020, 9, 21, 10, 11, 0, 0, UTC).toInstant() + + @AfterEach + fun afterEach() = clearAllMocks() + + @BeforeEach + fun beforeEach() { + mockkStatic(Instant::class) + every { Instant.now() } returns mockNow + } + + @Test + fun `doi registration`( + @MockK requestSpec: RequestBodySpec, + ) { + val bodySlot = slot>() + val org = section("Organization", "o1", "Name" to "EMBL") + val author = section("Author", null, "Name" to "John Doe", "Affiliation" to "o1", "ORCID" to "12-32-45-82") + val rootSection = ExtSection(type = "Study", sections = listOf(left(org), left(author))) + val submission = basicExtSubmission.copy(section = rootSection) + + every { webClient.post().uri(properties.endpoint) } returns requestSpec + every { requestSpec.bodyValue(capture(bodySlot)) } returns requestSpec + every { requestSpec.retrieve().bodyToMono(String::class.java).block() } returns "OK" + + testInstance.registerDoi(submission) + + val body = bodySlot.captured + val requestFile = body[FILE_PARAM]!!.first() as FileSystemResource + val expectedXml = Files.readString(Paths.get("src/test/resources/ExpectedDOIRequest.xml")) + assertThat(requestFile.file.readText()).isEqualToIgnoringWhitespace(expectedXml) + assertThat(body[USER_PARAM]!!.first()).isEqualTo(properties.user) + assertThat(body[PASSWORD_PARAM]!!.first()).isEqualTo(properties.password) + assertThat(body[OPERATION_PARAM]!!.first()).isEqualTo(OPERATION_PARAM_VALUE) + verify(exactly = 1) { + webClient.post().uri(properties.endpoint) + requestSpec.bodyValue(body) + requestSpec.retrieve().bodyToMono(String::class.java).block() + } + } + + @Test + fun `missing title`() { + val submission = basicExtSubmission.copy(title = null) + val exception = assertThrows { testInstance.registerDoi(submission) } + + verify(exactly = 0) { webClient.post() } + assertThat(exception.message).isEqualTo("A title is required for DOI registration") + } + + @Test + fun `missing organization`() { + val submission = basicExtSubmission + val exception = assertThrows { testInstance.registerDoi(submission) } + + verify(exactly = 0) { webClient.post() } + assertThat(exception.message).isEqualTo("The required DOI field 'organization' could not be found") + } + + @Test + fun `missing organization accNo`() { + val org = section("Organization", null, "Name" to "EMBL") + val rootSection = ExtSection(type = "Study", sections = listOf(left(org))) + val submission = basicExtSubmission.copy(section = rootSection) + val exception = assertThrows { testInstance.registerDoi(submission) } + + verify(exactly = 0) { webClient.post() } + assertThat(exception.message).isEqualTo("Organizations are required to have an accession") + } + + @Test + fun `missing organization name`() { + val org1 = section("Organization", "o1", "Institute" to "EMBL") + val org2 = section("Organization", "o2", "Name" to "EMBL-EBI") + val org3 = section("Organization", "o3", "Institute" to "American Society") + val rootSection = ExtSection(type = "Study", sections = listOf(left(org1), left(org2), left(org3))) + val submission = basicExtSubmission.copy(section = rootSection) + val exception = assertThrows { testInstance.registerDoi(submission) } + + verify(exactly = 0) { webClient.post() } + assertThat(exception.message).isEqualTo("The following organization names are empty: o1, o3") + } + + @Test + fun `missing author name`() { + val org = section("Organization", "o1", "Name" to "EMBL") + val author = section("Author", null, "P.I." to "John Doe", "Affiliation" to "o1", "ORCID" to "12-32-45-82") + val rootSection = ExtSection(type = "Study", sections = listOf(left(org), left(author))) + val submission = basicExtSubmission.copy(section = rootSection) + val exception = assertThrows { testInstance.registerDoi(submission) } + + verify(exactly = 0) { webClient.post() } + assertThat(exception.message).isEqualTo("Authors are required to have a name") + } + + @Test + fun `missing affiliation`() { + val org = section("Organization", "o1", "Name" to "EMBL") + val author = section("Author", null, "Name" to "John Doe", "ORCID" to "12-32-45-82") + val rootSection = ExtSection(type = "Study", sections = listOf(left(org), left(author))) + val submission = basicExtSubmission.copy(section = rootSection) + val exception = assertThrows { testInstance.registerDoi(submission) } + + verify(exactly = 0) { webClient.post() } + assertThat(exception.message).isEqualTo("Authors are required to have an affiliation") + } + + @Test + fun `invalid affiliation`() { + val org = section("Organization", "o1", "Name" to "EMBL") + val author = section("Author", null, "Name" to "John Doe", "Affiliation" to "o2", "ORCID" to "12-32-45-82") + val rootSection = ExtSection(type = "Study", sections = listOf(left(org), left(author))) + val submission = basicExtSubmission.copy(section = rootSection) + val exception = assertThrows { testInstance.registerDoi(submission) } + + verify(exactly = 0) { webClient.post() } + assertThat(exception.message) + .isEqualTo("The organization 'o2' affiliated to the author 'John Doe' could not be found") + } + + private fun section(type: String, accNo: String?, vararg attributes: Pair) = + ExtSection(type = type, accNo = accNo, attributes = attributes.map { ExtAttribute(it.first, it.second) }) + + companion object { + private val properties = DoiProperties( + endpoint = "https://test-endpoint.org", + uiUrl = "https://www.biostudies.ac.uk", + user = "a-user", + password = "a-password", + ) + } +} diff --git a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt index 851e610b93..a1248aff9c 100644 --- a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt +++ b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt @@ -4,7 +4,10 @@ import ac.uk.ebi.biostd.persistence.common.request.ExtSubmitRequest import ac.uk.ebi.biostd.persistence.common.service.SubmissionDraftPersistenceService import ac.uk.ebi.biostd.submission.exceptions.InvalidSubmissionException import ac.uk.ebi.biostd.submission.model.SubmitRequest +import ac.uk.ebi.biostd.submission.service.DoiService import ac.uk.ebi.biostd.submission.validator.collection.CollectionValidationService +import ebi.ac.uk.extended.model.ExtAttribute +import ebi.ac.uk.model.constants.SubFields.DOI_REQUESTED import ebi.ac.uk.test.basicExtSubmission import io.mockk.clearAllMocks import io.mockk.every @@ -20,12 +23,14 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(MockKExtension::class) class SubmissionSubmitterTest( + @MockK private val doiService: DoiService, @MockK private val submissionSubmitter: ExtSubmissionSubmitter, @MockK private val submissionProcessor: SubmissionProcessor, @MockK private val collectionValidationService: CollectionValidationService, @MockK private val draftService: SubmissionDraftPersistenceService, ) { private val testInstance = SubmissionSubmitter( + doiService, submissionSubmitter, submissionProcessor, collectionValidationService, @@ -68,6 +73,47 @@ class SubmissionSubmitterTest( submissionSubmitter.createRequest(extRequest) draftService.setAcceptedStatus("TMP_123") } + verify(exactly = 0) { + draftService.setActiveStatus("TMP_123") + doiService.registerDoi(submission) + } + } + + @Test + fun `create request with doi`( + @MockK request: SubmitRequest, + ) { + val submission = basicExtSubmission.copy(attributes = listOf(ExtAttribute(DOI_REQUESTED.value, ""))) + val extRequestSlot = slot() + + every { request.draftKey } returns "TMP_123" + every { request.owner } returns submission.owner + every { request.accNo } returns submission.accNo + every { doiService.registerDoi(submission) } answers { nothing } + every { submissionProcessor.processSubmission(request) } returns submission + every { draftService.setAcceptedStatus("TMP_123") } answers { nothing } + every { collectionValidationService.executeCollectionValidators(submission) } answers { nothing } + every { draftService.setActiveStatus("TMP_123") } answers { nothing } + every { draftService.setProcessingStatus(submission.owner, "TMP_123") } answers { nothing } + every { draftService.setAcceptedStatus("S-TEST123") } answers { nothing } + every { draftService.deleteSubmissionDraft(submission.submitter, "S-TEST123") } answers { nothing } + every { + submissionSubmitter.createRequest(capture(extRequestSlot)) + } returns (submission.accNo to submission.version) + + testInstance.createRequest(request) + + val extRequest = extRequestSlot.captured + assertThat(extRequest.draftKey).isEqualTo("TMP_123") + assertThat(extRequest.submission).isEqualTo(submission) + verify(exactly = 1) { + submissionProcessor.processSubmission(request) + collectionValidationService.executeCollectionValidators(submission) + doiService.registerDoi(submission) + draftService.setProcessingStatus(submission.owner, "TMP_123") + submissionSubmitter.createRequest(extRequest) + draftService.setAcceptedStatus("TMP_123") + } verify(exactly = 0) { draftService.setActiveStatus("TMP_123") } diff --git a/submission/submitter/src/test/resources/ExpectedDOIRequest.xml b/submission/submitter/src/test/resources/ExpectedDOIRequest.xml new file mode 100644 index 0000000000..13cca2df33 --- /dev/null +++ b/submission/submitter/src/test/resources/ExpectedDOIRequest.xml @@ -0,0 +1,37 @@ + + + 1600683060 + 1600683060 + + EMBL-EBI + biostudies@ebi.ac.uk + + EMBL-EBI + + + + + + BioStudies Database + + + + + + John + Doe + EMBL + 12-32-45-82 + + + + Test Submission + + + 10.6019/S-TEST123 + https://www.biostudies.ac.uk/studies/S-TEST123 + + + + + From 9055058466380bfdb239edc44256192bc0bf3851 Mon Sep 17 00:00:00 2001 From: Jhoan Munoz Date: Wed, 2 Aug 2023 14:45:36 +0100 Subject: [PATCH 2/7] Pivotal ID # 184977702: Assign DOIs Automatically Avoid requesting already existing DOI records --- .../submitter/SubmissionSubmitter.kt | 6 +- .../submitter/SubmissionSubmitterTest.kt | 88 ++++++++++++------- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt index 08377d0966..871095aad9 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt @@ -6,6 +6,7 @@ import ac.uk.ebi.biostd.submission.exceptions.InvalidSubmissionException import ac.uk.ebi.biostd.submission.model.SubmitRequest import ac.uk.ebi.biostd.submission.service.DoiService import ac.uk.ebi.biostd.submission.validator.collection.CollectionValidationService +import ebi.ac.uk.base.orFalse import ebi.ac.uk.extended.events.RequestCheckedReleased import ebi.ac.uk.extended.events.RequestCleaned import ebi.ac.uk.extended.events.RequestCreated @@ -77,7 +78,8 @@ class SubmissionSubmitter( rqt.draftKey?.let { startProcessingDraft(rqt.accNo, rqt.owner, it) } val processed = submissionProcessor.processSubmission(rqt) collectionValidationService.executeCollectionValidators(processed) - if (processed.requiresDoi) doiService.registerDoi(processed) + val doiPreviouslyRequested = rqt.previousVersion?.doiRequested.orFalse() + if (doiPreviouslyRequested.not() && processed.doiRequested) doiService.registerDoi(processed) rqt.draftKey?.let { acceptDraft(rqt.accNo, rqt.owner, it) } logger.info { "${rqt.accNo} ${rqt.owner} Finished processing submission request" } @@ -104,6 +106,6 @@ class SubmissionSubmitter( logger.info { "$accNo $owner Status of draft with key '$draftKey' set to ACTIVE" } } - private val ExtSubmission.requiresDoi: Boolean + private val ExtSubmission.doiRequested: Boolean get() = attributes.find { it.name == DOI_REQUESTED.value } != null } diff --git a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt index a1248aff9c..2ebd54969d 100644 --- a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt +++ b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt @@ -17,12 +17,14 @@ import io.mockk.slot import io.mockk.verify import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(MockKExtension::class) class SubmissionSubmitterTest( + @MockK private val request: SubmitRequest, @MockK private val doiService: DoiService, @MockK private val submissionSubmitter: ExtSubmissionSubmitter, @MockK private val submissionProcessor: SubmissionProcessor, @@ -40,23 +42,19 @@ class SubmissionSubmitterTest( @AfterEach fun afterEach() = clearAllMocks() + @BeforeEach + fun beforeEach() { + setUpRequest() + setUpDraftService() + } + @Test - fun `create request`( - @MockK request: SubmitRequest, - ) { + fun `create request`() { val submission = basicExtSubmission val extRequestSlot = slot() - every { request.draftKey } returns "TMP_123" - every { request.owner } returns submission.owner - every { request.accNo } returns submission.accNo every { submissionProcessor.processSubmission(request) } returns submission - every { draftService.setAcceptedStatus("TMP_123") } answers { nothing } every { collectionValidationService.executeCollectionValidators(submission) } answers { nothing } - every { draftService.setActiveStatus("TMP_123") } answers { nothing } - every { draftService.setProcessingStatus(submission.owner, "TMP_123") } answers { nothing } - every { draftService.setAcceptedStatus("S-TEST123") } answers { nothing } - every { draftService.deleteSubmissionDraft(submission.submitter, "S-TEST123") } answers { nothing } every { submissionSubmitter.createRequest(capture(extRequestSlot)) } returns (submission.accNo to submission.version) @@ -80,23 +78,13 @@ class SubmissionSubmitterTest( } @Test - fun `create request with doi`( - @MockK request: SubmitRequest, - ) { + fun `create request with doi`() { val submission = basicExtSubmission.copy(attributes = listOf(ExtAttribute(DOI_REQUESTED.value, ""))) val extRequestSlot = slot() - every { request.draftKey } returns "TMP_123" - every { request.owner } returns submission.owner - every { request.accNo } returns submission.accNo every { doiService.registerDoi(submission) } answers { nothing } every { submissionProcessor.processSubmission(request) } returns submission - every { draftService.setAcceptedStatus("TMP_123") } answers { nothing } every { collectionValidationService.executeCollectionValidators(submission) } answers { nothing } - every { draftService.setActiveStatus("TMP_123") } answers { nothing } - every { draftService.setProcessingStatus(submission.owner, "TMP_123") } answers { nothing } - every { draftService.setAcceptedStatus("S-TEST123") } answers { nothing } - every { draftService.deleteSubmissionDraft(submission.submitter, "S-TEST123") } answers { nothing } every { submissionSubmitter.createRequest(capture(extRequestSlot)) } returns (submission.accNo to submission.version) @@ -120,18 +108,41 @@ class SubmissionSubmitterTest( } @Test - fun `create with failure on validation`( - @MockK request: SubmitRequest, - ) { + fun `create request with doi already existing`() { + val submission = basicExtSubmission.copy(attributes = listOf(ExtAttribute(DOI_REQUESTED.value, ""))) + val extRequestSlot = slot() + + every { request.previousVersion } returns submission + every { doiService.registerDoi(submission) } answers { nothing } + every { submissionProcessor.processSubmission(request) } returns submission + every { collectionValidationService.executeCollectionValidators(submission) } answers { nothing } + every { + submissionSubmitter.createRequest(capture(extRequestSlot)) + } returns (submission.accNo to submission.version) + + testInstance.createRequest(request) + + val extRequest = extRequestSlot.captured + assertThat(extRequest.draftKey).isEqualTo("TMP_123") + assertThat(extRequest.submission).isEqualTo(submission) + verify(exactly = 1) { + submissionProcessor.processSubmission(request) + collectionValidationService.executeCollectionValidators(submission) + draftService.setProcessingStatus(submission.owner, "TMP_123") + submissionSubmitter.createRequest(extRequest) + draftService.setAcceptedStatus("TMP_123") + } + verify(exactly = 0) { + doiService.registerDoi(submission) + draftService.setActiveStatus("TMP_123") + } + } + + @Test + fun `create with failure on validation`() { val submission = basicExtSubmission val extRequestSlot = slot() - every { request.draftKey } returns "TMP_123" - every { request.owner } returns submission.owner - every { request.accNo } returns submission.accNo - every { draftService.setActiveStatus("TMP_123") } answers { nothing } - every { draftService.setProcessingStatus(submission.owner, "TMP_123") } answers { nothing } - every { draftService.setAcceptedStatus("TMP_123") } answers { nothing } every { submissionProcessor.processSubmission(request) } throws RuntimeException("validation error") assertThrows { testInstance.createRequest(request) } @@ -147,4 +158,19 @@ class SubmissionSubmitterTest( draftService.setAcceptedStatus("TMP_123") } } + + private fun setUpRequest() { + every { request.draftKey } returns "TMP_123" + every { request.owner } returns basicExtSubmission.owner + every { request.accNo } returns basicExtSubmission.accNo + every { request.previousVersion } returns null + } + + private fun setUpDraftService() { + every { draftService.setAcceptedStatus("TMP_123") } answers { nothing } + every { draftService.setActiveStatus("TMP_123") } answers { nothing } + every { draftService.setAcceptedStatus("S-TEST123") } answers { nothing } + every { draftService.setProcessingStatus(basicExtSubmission.owner, "TMP_123") } answers { nothing } + every { draftService.deleteSubmissionDraft(basicExtSubmission.submitter, "S-TEST123") } answers { nothing } + } } From c9edc6155b838984734257d3f3281a7735d10533 Mon Sep 17 00:00:00 2001 From: Jhoan Munoz Date: Thu, 17 Aug 2023 16:17:33 +0100 Subject: [PATCH 3/7] Pivotal ID # 184977702: Assign DOIs Automatically - Calculate the DOI as part of the submission processing - Add an attribute to the extended model in order to keep track of the DOIs - Add logic to handle previous versions --- .../ebi/ac/uk/model/constants/Fields.kt | 2 +- .../ebi/ac/uk/extended/model/ExtendedModel.kt | 2 + .../ebi/ac/uk/test/ExtSubmissionFactory.kt | 1 + .../submitter/SubmissionSubmitterTest.kt | 67 ----- .../submission/exceptions/DoiExceptions.kt | 2 + .../ebi/biostd/submission/model/DoiRequest.kt | 5 +- .../biostd/submission/service/DoiService.kt | 46 ++- .../submitter/SubmissionProcessor.kt | 4 + .../submitter/SubmissionSubmitter.kt | 9 - .../submission/service/DoiServiceTest.kt | 263 +++++++++++++++--- 10 files changed, 267 insertions(+), 134 deletions(-) diff --git a/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/constants/Fields.kt b/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/constants/Fields.kt index e6cd44d0b0..82ba72541e 100644 --- a/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/constants/Fields.kt +++ b/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/constants/Fields.kt @@ -46,7 +46,7 @@ enum class SubFields(override val value: String) : Fields { TITLE("Title"), ROOT_PATH("RootPath"), PUBLIC_ACCESS_TAG("Public"), - DOI_REQUESTED("RequestDOI"), + DOI("DOI"), RELEASE_DATE("ReleaseDate"), RELEASE_TIME("rtime"), diff --git a/commons/commons-model-extended/src/main/kotlin/ebi/ac/uk/extended/model/ExtendedModel.kt b/commons/commons-model-extended/src/main/kotlin/ebi/ac/uk/extended/model/ExtendedModel.kt index a235c680e1..1db594a656 100644 --- a/commons/commons-model-extended/src/main/kotlin/ebi/ac/uk/extended/model/ExtendedModel.kt +++ b/commons/commons-model-extended/src/main/kotlin/ebi/ac/uk/extended/model/ExtendedModel.kt @@ -112,6 +112,8 @@ data class ExtSubmission( var schemaVersion: String, val submitter: String, val title: String?, + // TODO doi mappers + val doi: String?, val method: ExtSubmissionMethod, val relPath: String, val rootPath: String?, diff --git a/commons/commons-test/src/main/kotlin/ebi/ac/uk/test/ExtSubmissionFactory.kt b/commons/commons-test/src/main/kotlin/ebi/ac/uk/test/ExtSubmissionFactory.kt index aaff5e7c67..32f7b12174 100644 --- a/commons/commons-test/src/main/kotlin/ebi/ac/uk/test/ExtSubmissionFactory.kt +++ b/commons/commons-test/src/main/kotlin/ebi/ac/uk/test/ExtSubmissionFactory.kt @@ -12,6 +12,7 @@ val basicExtSubmission = ExtSubmission( version = 1, schemaVersion = "1.0", title = "Test Submission", + doi = null, owner = "owner@email.org", submitter = "submitter@email.org", method = PAGE_TAB, diff --git a/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt b/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt index 1d587534e5..f8f8efd512 100644 --- a/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt +++ b/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitterTest.kt @@ -4,10 +4,7 @@ import ac.uk.ebi.biostd.persistence.common.request.ExtSubmitRequest import ac.uk.ebi.biostd.persistence.common.service.SubmissionDraftPersistenceService import ac.uk.ebi.biostd.submission.exceptions.InvalidSubmissionException import ac.uk.ebi.biostd.submission.model.SubmitRequest -import ac.uk.ebi.biostd.submission.service.DoiService import ac.uk.ebi.biostd.submission.validator.collection.CollectionValidationService -import ebi.ac.uk.extended.model.ExtAttribute -import ebi.ac.uk.model.constants.SubFields.DOI_REQUESTED import ebi.ac.uk.test.basicExtSubmission import io.mockk.clearAllMocks import io.mockk.coEvery @@ -29,14 +26,12 @@ import org.junit.jupiter.api.extension.ExtendWith @OptIn(ExperimentalCoroutinesApi::class) class SubmissionSubmitterTest( @MockK private val request: SubmitRequest, - @MockK private val doiService: DoiService, @MockK private val submissionSubmitter: ExtSubmissionSubmitter, @MockK private val submissionProcessor: SubmissionProcessor, @MockK private val collectionValidationService: CollectionValidationService, @MockK private val draftService: SubmissionDraftPersistenceService, ) { private val testInstance = SubmissionSubmitter( - doiService, submissionSubmitter, submissionProcessor, collectionValidationService, @@ -77,68 +72,6 @@ class SubmissionSubmitterTest( } coVerify(exactly = 0) { draftService.setActiveStatus("TMP_123") - doiService.registerDoi(submission) - } - } - - @Test - fun `create request with doi`() = runTest { - val submission = basicExtSubmission.copy(attributes = listOf(ExtAttribute(DOI_REQUESTED.value, ""))) - val extRequestSlot = slot() - - every { doiService.registerDoi(submission) } answers { nothing } - every { submissionProcessor.processSubmission(request) } returns submission - every { collectionValidationService.executeCollectionValidators(submission) } answers { nothing } - every { - submissionSubmitter.createRequest(capture(extRequestSlot)) - } returns (submission.accNo to submission.version) - - testInstance.createRequest(request) - - val extRequest = extRequestSlot.captured - assertThat(extRequest.draftKey).isEqualTo("TMP_123") - assertThat(extRequest.submission).isEqualTo(submission) - coVerify(exactly = 1) { - submissionProcessor.processSubmission(request) - collectionValidationService.executeCollectionValidators(submission) - doiService.registerDoi(submission) - draftService.setProcessingStatus(submission.owner, "TMP_123") - submissionSubmitter.createRequest(extRequest) - draftService.setAcceptedStatus("TMP_123") - } - coVerify(exactly = 0) { - draftService.setActiveStatus("TMP_123") - } - } - - @Test - fun `create request with doi already existing`() = runTest { - val submission = basicExtSubmission.copy(attributes = listOf(ExtAttribute(DOI_REQUESTED.value, ""))) - val extRequestSlot = slot() - - every { request.previousVersion } returns submission - every { doiService.registerDoi(submission) } answers { nothing } - every { submissionProcessor.processSubmission(request) } returns submission - every { collectionValidationService.executeCollectionValidators(submission) } answers { nothing } - every { - submissionSubmitter.createRequest(capture(extRequestSlot)) - } returns (submission.accNo to submission.version) - - testInstance.createRequest(request) - - val extRequest = extRequestSlot.captured - assertThat(extRequest.draftKey).isEqualTo("TMP_123") - assertThat(extRequest.submission).isEqualTo(submission) - coVerify(exactly = 1) { - submissionProcessor.processSubmission(request) - collectionValidationService.executeCollectionValidators(submission) - draftService.setProcessingStatus(submission.owner, "TMP_123") - submissionSubmitter.createRequest(extRequest) - draftService.setAcceptedStatus("TMP_123") - } - coVerify(exactly = 0) { - doiService.registerDoi(submission) - draftService.setActiveStatus("TMP_123") } } diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt index cce0ac150f..35c0c7e7c0 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt @@ -18,3 +18,5 @@ class InvalidAuthorAffiliationException( author: String, organization: String, ) : RuntimeException("The organization '$organization' affiliated to the author '$author' could not be found") + +class InvalidDoiException : RuntimeException("The given DOI should match the previous version") diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt index b5b1b43b0a..aaf1d3ec61 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt @@ -17,6 +17,9 @@ internal class DoiRequest( private val instanceUrl: String, private val contributors: List, ) { + val doi: String + get() = "$BS_DOI_ID/$accNo" + fun asXmlRequest(): String { val timestamp = Instant.now().epochSecond.toString() return xml("doi_batch") { @@ -62,7 +65,7 @@ internal class DoiRequest( "title" { -title } } "doi_data" { - "doi" { -"$BS_DOI_ID/$accNo" } + "doi" { -doi } "resource" { -"$instanceUrl/studies/$accNo" } } } diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt index 0dde6a9dbf..89f13bea1b 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt @@ -3,6 +3,7 @@ package ac.uk.ebi.biostd.submission.service import ac.uk.ebi.biostd.common.properties.DoiProperties import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorAffiliationException import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorNameException +import ac.uk.ebi.biostd.submission.exceptions.InvalidDoiException import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgException import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgNamesException import ac.uk.ebi.biostd.submission.exceptions.MissingAuthorAffiliationException @@ -10,13 +11,15 @@ import ac.uk.ebi.biostd.submission.exceptions.MissingDoiFieldException import ac.uk.ebi.biostd.submission.exceptions.MissingTitleException import ac.uk.ebi.biostd.submission.model.Contributor import ac.uk.ebi.biostd.submission.model.DoiRequest +import ac.uk.ebi.biostd.submission.model.SubmitRequest import ebi.ac.uk.commons.http.ext.RequestParams import ebi.ac.uk.commons.http.ext.post -import ebi.ac.uk.extended.model.ExtSection -import ebi.ac.uk.extended.model.ExtSubmission -import ebi.ac.uk.extended.model.allSections -import ebi.ac.uk.extended.model.computedTitle import ebi.ac.uk.io.FileUtils +import ebi.ac.uk.model.Section +import ebi.ac.uk.model.Submission +import ebi.ac.uk.model.constants.SubFields.DOI +import ebi.ac.uk.model.extensions.allSections +import ebi.ac.uk.model.extensions.title import ebi.ac.uk.util.collections.ifNotEmpty import mu.KotlinLogging import org.springframework.core.io.FileSystemResource @@ -45,10 +48,23 @@ class DoiService( private val webClient: WebClient, private val properties: DoiProperties, ) { - fun registerDoi(sub: ExtSubmission) { - val title = requireNotNull(sub.computedTitle) { throw MissingTitleException() } - val request = DoiRequest(sub.accNo, title, properties.uiUrl, getContributors(sub)) - val requestFile = Files.createTempFile("${TEMP_FILE_NAME}_${sub.accNo}", ".xml").toFile() + fun calculateDoi(accNo: String, rqt: SubmitRequest): String? { + val doi = rqt.submission.find(DOI) ?: return null + val previousDoi = rqt.previousVersion?.doi + + if (previousDoi != null) { + require(doi == previousDoi) { throw InvalidDoiException() } + return doi + } + + return registerDoi(accNo, rqt) + } + + private fun registerDoi(accNo: String, rqt: SubmitRequest): String { + val sub = rqt.submission + val title = requireNotNull(sub.title) { throw MissingTitleException() } + val request = DoiRequest(accNo, title, properties.uiUrl, getContributors(sub)) + val requestFile = Files.createTempFile("${TEMP_FILE_NAME}_${accNo}", ".xml").toFile() FileUtils.writeContent(requestFile, request.asXmlRequest()) val body = LinkedMultiValueMap().apply { @@ -58,19 +74,21 @@ class DoiService( add(FILE_PARAM, FileSystemResource(requestFile)) } - logger.info { "${sub.accNo} ${sub.owner} Registering DOI" } + logger.info { "$accNo ${rqt.owner} Registering DOI" } webClient.post(properties.endpoint, RequestParams(body = body)) + + return request.doi } - private fun getContributors(sub: ExtSubmission): List { + private fun getContributors(sub: Submission): List { val organizations = getOrganizations(sub) - return sub.allSections + return sub.allSections() .filter { it.type.lowercase() == AUTHOR_TYPE } .ifEmpty { throw MissingDoiFieldException(AUTHOR_TYPE) } .map { it.asContributor(organizations) } } - private fun ExtSection.asContributor(orgs: Map): Contributor { + private fun Section.asContributor(orgs: Map): Contributor { val attrsMap = attributes.associateBy({ it.name.lowercase() }, { it.value }) val names = requireNotNull(attrsMap[NAME_ATTR]) { throw InvalidAuthorNameException() } val affiliation = requireNotNull(attrsMap[AFFILIATION_ATTR]) { throw MissingAuthorAffiliationException() } @@ -84,8 +102,8 @@ class DoiService( ) } - private fun getOrganizations(sub: ExtSubmission): Map { - val organizations = sub.allSections + private fun getOrganizations(sub: Submission): Map { + val organizations = sub.allSections() .filter { it.type.lowercase() == ORG_TYPE } .ifEmpty { throw MissingDoiFieldException(ORG_TYPE) } .associateBy( diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionProcessor.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionProcessor.kt index a11f67d8d7..7f7e354379 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionProcessor.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionProcessor.kt @@ -5,6 +5,7 @@ import ac.uk.ebi.biostd.persistence.common.service.SubmissionPersistenceService import ac.uk.ebi.biostd.submission.model.SubmitRequest import ac.uk.ebi.biostd.submission.service.AccNoService import ac.uk.ebi.biostd.submission.service.CollectionProcessor +import ac.uk.ebi.biostd.submission.service.DoiService import ac.uk.ebi.biostd.submission.service.TimesService import ebi.ac.uk.extended.mapping.from.ToExtSectionMapper import ebi.ac.uk.extended.mapping.from.toExtAttributes @@ -27,6 +28,7 @@ private val logger = KotlinLogging.logger {} @Suppress("LongParameterList") class SubmissionProcessor( + private val doiService: DoiService, private val persistenceService: SubmissionPersistenceService, private val timesService: TimesService, private val accNoService: AccNoService, @@ -42,6 +44,7 @@ class SubmissionProcessor( logger.info { "${rqt.accNo} ${rqt.owner} Assigned accNo '$accNoString' to draft with key '${rqt.draftKey}'" } + val doi = doiService.calculateDoi(accNoString, rqt) val version = persistenceService.getNextVersion(accNoString) val secretKey = previousVersion?.secretKey ?: UUID.randomUUID().toString() val relPath = accNoService.getRelPath(accNo) @@ -55,6 +58,7 @@ class SubmissionProcessor( schemaVersion = DEFAULT_SCHEMA_VERSION, method = getMethod(method), title = submission.title, + doi = doi, relPath = relPath, rootPath = submission.rootPath, released = released, diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt index d5207cd092..e5315b3a26 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt @@ -4,9 +4,7 @@ import ac.uk.ebi.biostd.persistence.common.request.ExtSubmitRequest import ac.uk.ebi.biostd.persistence.common.service.SubmissionDraftPersistenceService import ac.uk.ebi.biostd.submission.exceptions.InvalidSubmissionException import ac.uk.ebi.biostd.submission.model.SubmitRequest -import ac.uk.ebi.biostd.submission.service.DoiService import ac.uk.ebi.biostd.submission.validator.collection.CollectionValidationService -import ebi.ac.uk.base.orFalse import ebi.ac.uk.extended.events.RequestCheckedReleased import ebi.ac.uk.extended.events.RequestCleaned import ebi.ac.uk.extended.events.RequestCreated @@ -15,14 +13,12 @@ import ebi.ac.uk.extended.events.RequestIndexed import ebi.ac.uk.extended.events.RequestLoaded import ebi.ac.uk.extended.events.RequestPersisted import ebi.ac.uk.extended.model.ExtSubmission -import ebi.ac.uk.model.constants.SubFields.DOI_REQUESTED import mu.KotlinLogging private val logger = KotlinLogging.logger {} @Suppress("TooManyFunctions") class SubmissionSubmitter( - private val doiService: DoiService, private val submissionSubmitter: ExtSubmissionSubmitter, private val submissionProcessor: SubmissionProcessor, private val collectionValidationService: CollectionValidationService, @@ -78,8 +74,6 @@ class SubmissionSubmitter( rqt.draftKey?.let { startProcessingDraft(rqt.accNo, rqt.owner, it) } val processed = submissionProcessor.processSubmission(rqt) collectionValidationService.executeCollectionValidators(processed) - val doiPreviouslyRequested = rqt.previousVersion?.doiRequested.orFalse() - if (doiPreviouslyRequested.not() && processed.doiRequested) doiService.registerDoi(processed) rqt.draftKey?.let { acceptDraft(rqt.accNo, rqt.owner, it) } logger.info { "${rqt.accNo} ${rqt.owner} Finished processing submission request" } @@ -105,7 +99,4 @@ class SubmissionSubmitter( draftService.setActiveStatus(draftKey) logger.info { "$accNo $owner Status of draft with key '$draftKey' set to ACTIVE" } } - - private val ExtSubmission.doiRequested: Boolean - get() = attributes.find { it.name == DOI_REQUESTED.value } != null } diff --git a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt index d2dca971c9..8e6a0fc331 100644 --- a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt +++ b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt @@ -3,15 +3,19 @@ package ac.uk.ebi.biostd.submission.service import ac.uk.ebi.biostd.common.properties.DoiProperties import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorAffiliationException import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorNameException +import ac.uk.ebi.biostd.submission.exceptions.InvalidDoiException import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgException import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgNamesException import ac.uk.ebi.biostd.submission.exceptions.MissingAuthorAffiliationException import ac.uk.ebi.biostd.submission.exceptions.MissingDoiFieldException import ac.uk.ebi.biostd.submission.exceptions.MissingTitleException -import arrow.core.Either.Companion.left -import ebi.ac.uk.extended.model.ExtAttribute -import ebi.ac.uk.extended.model.ExtSection -import ebi.ac.uk.test.basicExtSubmission +import ac.uk.ebi.biostd.submission.model.DoiRequest.Companion.BS_DOI_ID +import ac.uk.ebi.biostd.submission.model.SubmitRequest +import ebi.ac.uk.dsl.attribute +import ebi.ac.uk.dsl.section +import ebi.ac.uk.dsl.submission +import ebi.ac.uk.extended.model.ExtSubmission +import ebi.ac.uk.model.extensions.title import io.mockk.clearAllMocks import io.mockk.every import io.mockk.impl.annotations.MockK @@ -38,6 +42,8 @@ import java.time.ZoneOffset.UTC @ExtendWith(MockKExtension::class) class DoiServiceTest( @MockK private val webClient: WebClient, + @MockK private val submitRequest: SubmitRequest, + @MockK private val previousVersion: ExtSubmission, ) { private val testInstance = DoiService(webClient, properties) private val mockNow = OffsetDateTime.of(2020, 9, 21, 10, 11, 0, 0, UTC).toInstant() @@ -49,6 +55,7 @@ class DoiServiceTest( fun beforeEach() { mockkStatic(Instant::class) every { Instant.now() } returns mockNow + every { submitRequest.previousVersion } returns null } @Test @@ -56,20 +63,35 @@ class DoiServiceTest( @MockK requestSpec: RequestBodySpec, ) { val bodySlot = slot>() - val org = section("Organization", "o1", "Name" to "EMBL") - val author = section("Author", null, "Name" to "John Doe", "Affiliation" to "o1", "ORCID" to "12-32-45-82") - val rootSection = ExtSection(type = "Study", sections = listOf(left(org), left(author))) - val submission = basicExtSubmission.copy(section = rootSection) + val submission = submission { + title = "Test Submission" + attribute("DOI", "") + section("Study") { + section("Organization") { + accNo = "o1" + attribute("Name", "EMBL") + } + + section("Author") { + attribute("Name", "John Doe") + attribute("ORCID", "12-32-45-82") + attribute("Affiliation", "o1", ref = true) + } + } + } + + every { submitRequest.submission } returns submission every { webClient.post().uri(properties.endpoint) } returns requestSpec every { requestSpec.bodyValue(capture(bodySlot)) } returns requestSpec every { requestSpec.retrieve().bodyToMono(String::class.java).block() } returns "OK" - testInstance.registerDoi(submission) - + val doi = testInstance.calculateDoi(TEST_ACC_NO, submitRequest) val body = bodySlot.captured val requestFile = body[FILE_PARAM]!!.first() as FileSystemResource val expectedXml = Files.readString(Paths.get("src/test/resources/ExpectedDOIRequest.xml")) + + assertThat(doi).isEqualTo("$BS_DOI_ID/$TEST_ACC_NO") assertThat(requestFile.file.readText()).isEqualToIgnoringWhitespace(expectedXml) assertThat(body[USER_PARAM]!!.first()).isEqualTo(properties.user) assertThat(body[PASSWORD_PARAM]!!.first()).isEqualTo(properties.password) @@ -81,10 +103,74 @@ class DoiServiceTest( } } + @Test + fun `doi not requested`() { + val submission = submission { + title = "Test Submission" + + section("Study") { + attribute("Type", "Experiment") + } + } + + every { submitRequest.submission } returns submission + + assertThat(testInstance.calculateDoi(TEST_ACC_NO, submitRequest)).isNull() + verify(exactly = 0) { webClient.post() } + } + + @Test + fun `already existing DOI`() { + val previousVersionDoi = "$BS_DOI_ID/$TEST_ACC_NO" + val submission = submission { + title = "Test Submission" + attribute("DOI", "$BS_DOI_ID/$TEST_ACC_NO") + + section("Study") { + attribute("Type", "Experiment") + } + } + + every { submitRequest.submission } returns submission + every { previousVersion.doi } returns previousVersionDoi + every { submitRequest.previousVersion } returns previousVersion + + val doi = testInstance.calculateDoi(TEST_ACC_NO, submitRequest) + + assertThat(doi).isEqualTo(previousVersionDoi) + verify(exactly = 0) { webClient.post() } + } + + @Test + fun `invalid given DOI`() { + val submission = submission { + title = "Test Submission" + attribute("DOI", "10.287.71/$TEST_ACC_NO") + + section("Study") { + attribute("Type", "Experiment") + } + } + + every { submitRequest.submission } returns submission + every { previousVersion.doi } returns "$BS_DOI_ID/$TEST_ACC_NO" + every { submitRequest.previousVersion } returns previousVersion + + val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } + + assertThat(exception.message).isEqualTo("The given DOI should match the previous version") + verify(exactly = 0) { webClient.post() } + } + @Test fun `missing title`() { - val submission = basicExtSubmission.copy(title = null) - val exception = assertThrows { testInstance.registerDoi(submission) } + val submission = submission { + attribute("DOI", "") + } + + every { submitRequest.submission } returns submission + + val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } verify(exactly = 0) { webClient.post() } assertThat(exception.message).isEqualTo("A title is required for DOI registration") @@ -92,8 +178,20 @@ class DoiServiceTest( @Test fun `missing organization`() { - val submission = basicExtSubmission - val exception = assertThrows { testInstance.registerDoi(submission) } + val submission = submission { + title = "Test Submission" + attribute("DOI", "") + + section("Study") { + section("Author") { + attribute("Name", "John Doe") + } + } + } + + every { submitRequest.submission } returns submission + + val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } verify(exactly = 0) { webClient.post() } assertThat(exception.message).isEqualTo("The required DOI field 'organization' could not be found") @@ -101,10 +199,20 @@ class DoiServiceTest( @Test fun `missing organization accNo`() { - val org = section("Organization", null, "Name" to "EMBL") - val rootSection = ExtSection(type = "Study", sections = listOf(left(org))) - val submission = basicExtSubmission.copy(section = rootSection) - val exception = assertThrows { testInstance.registerDoi(submission) } + val submission = submission { + title = "Test Submission" + attribute("DOI", "") + + section("Study") { + section("Organization") { + attribute("Name", "EMBL") + } + } + } + + every { submitRequest.submission } returns submission + + val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } verify(exactly = 0) { webClient.post() } assertThat(exception.message).isEqualTo("Organizations are required to have an accession") @@ -112,12 +220,31 @@ class DoiServiceTest( @Test fun `missing organization name`() { - val org1 = section("Organization", "o1", "Institute" to "EMBL") - val org2 = section("Organization", "o2", "Name" to "EMBL-EBI") - val org3 = section("Organization", "o3", "Institute" to "American Society") - val rootSection = ExtSection(type = "Study", sections = listOf(left(org1), left(org2), left(org3))) - val submission = basicExtSubmission.copy(section = rootSection) - val exception = assertThrows { testInstance.registerDoi(submission) } + val submission = submission { + title = "Test Submission" + attribute("DOI", "") + + section("Study") { + section("Organization") { + accNo = "o1" + attribute("Institue", "American Society") + } + + section("Organization") { + accNo = "o2" + attribute("Name", "EMBL") + } + + section("Organization") { + accNo = "o3" + attribute("Research Associate", "Astrazeneca") + } + } + } + + every { submitRequest.submission } returns submission + + val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } verify(exactly = 0) { webClient.post() } assertThat(exception.message).isEqualTo("The following organization names are empty: o1, o3") @@ -125,11 +252,29 @@ class DoiServiceTest( @Test fun `missing author name`() { - val org = section("Organization", "o1", "Name" to "EMBL") - val author = section("Author", null, "P.I." to "John Doe", "Affiliation" to "o1", "ORCID" to "12-32-45-82") - val rootSection = ExtSection(type = "Study", sections = listOf(left(org), left(author))) - val submission = basicExtSubmission.copy(section = rootSection) - val exception = assertThrows { testInstance.registerDoi(submission) } + val submission = submission { + title = "Test Submission" + attribute("DOI", "") + + section("Study") { + section("Organization") { + accNo = "o1" + attribute("Name", "EMBL") + } + + section("Author") { + attribute("P.I.", "John Doe") + attribute("ORCID", "12-32-45-82") + attribute("Affiliation", "o1", ref = true) + } + } + } + + every { submitRequest.submission } returns submission + + val exception = assertThrows { + testInstance.calculateDoi(TEST_ACC_NO, submitRequest) + } verify(exactly = 0) { webClient.post() } assertThat(exception.message).isEqualTo("Authors are required to have a name") @@ -137,11 +282,28 @@ class DoiServiceTest( @Test fun `missing affiliation`() { - val org = section("Organization", "o1", "Name" to "EMBL") - val author = section("Author", null, "Name" to "John Doe", "ORCID" to "12-32-45-82") - val rootSection = ExtSection(type = "Study", sections = listOf(left(org), left(author))) - val submission = basicExtSubmission.copy(section = rootSection) - val exception = assertThrows { testInstance.registerDoi(submission) } + val submission = submission { + title = "Test Submission" + attribute("DOI", "") + + section("Study") { + section("Organization") { + accNo = "o1" + attribute("Name", "EMBL") + } + + section("Author") { + attribute("Name", "John Doe") + attribute("ORCID", "12-32-45-82") + } + } + } + + every { submitRequest.submission } returns submission + + val exception = assertThrows { + testInstance.calculateDoi(TEST_ACC_NO, submitRequest) + } verify(exactly = 0) { webClient.post() } assertThat(exception.message).isEqualTo("Authors are required to have an affiliation") @@ -149,21 +311,38 @@ class DoiServiceTest( @Test fun `invalid affiliation`() { - val org = section("Organization", "o1", "Name" to "EMBL") - val author = section("Author", null, "Name" to "John Doe", "Affiliation" to "o2", "ORCID" to "12-32-45-82") - val rootSection = ExtSection(type = "Study", sections = listOf(left(org), left(author))) - val submission = basicExtSubmission.copy(section = rootSection) - val exception = assertThrows { testInstance.registerDoi(submission) } + val submission = submission { + title = "Test Submission" + attribute("DOI", "") + + section("Study") { + section("Organization") { + accNo = "o1" + attribute("Name", "EMBL") + } + + section("Author") { + attribute("Name", "John Doe") + attribute("ORCID", "12-32-45-82") + attribute("Affiliation", "o2", ref = true) + } + } + } + + every { submitRequest.submission } returns submission + + val exception = assertThrows { + testInstance.calculateDoi(TEST_ACC_NO, submitRequest) + } verify(exactly = 0) { webClient.post() } assertThat(exception.message) .isEqualTo("The organization 'o2' affiliated to the author 'John Doe' could not be found") } - private fun section(type: String, accNo: String?, vararg attributes: Pair) = - ExtSection(type = type, accNo = accNo, attributes = attributes.map { ExtAttribute(it.first, it.second) }) - companion object { + private const val TEST_ACC_NO = "S-TEST123" + private val properties = DoiProperties( endpoint = "https://test-endpoint.org", uiUrl = "https://www.biostudies.ac.uk", From 1602e552cad00a5c0db28a977c8d738cf8bb0999 Mon Sep 17 00:00:00 2001 From: Jhoan Munoz Date: Fri, 25 Aug 2023 17:21:58 +0100 Subject: [PATCH 4/7] Pivotal ID # 184977702: Assign DOIs Automatically Fields serialization --- .../constants/ExtSerializationFields.kt | 1 + .../serializers/ExtSubmissionSerializer.kt | 2 + .../ac/ebi/extended/test/SubmissionFactory.kt | 78 ++++++++++--------- .../ebi/ac/uk/extended/model/ExtendedModel.kt | 1 - .../converters/from/DocSubmissionConverter.kt | 2 + .../shared/ConverterCommonsFields.kt | 1 + .../db/converters/to/SubmissionConverter.kt | 2 + .../doc/mapping/from/ToDocSubmissionMapper.kt | 1 + .../doc/mapping/to/ToExtSubmissionMapper.kt | 1 + .../doc/migrations/DatabaseChangeLog.kt | 10 +++ .../persistence/doc/model/SubmissionModel.kt | 1 + .../converters/to/SubmissionConverterTest.kt | 2 + .../doc/migrations/DatabaseChangeLogTest.kt | 21 +++++ .../doc/test/SubmissionTestHelper.kt | 4 +- .../doc/test/doc/SubmissionDocTestHelper.kt | 1 + .../doc/test/doc/ext/ExtSubmissionFactory.kt | 2 + .../biostd/common/config/SubmitterConfig.kt | 4 +- .../ebi/biostd/submission/model/DoiRequest.kt | 17 +++- .../biostd/submission/service/DoiService.kt | 43 +++++----- .../submitter/SubmissionSubmitter.kt | 1 + .../submission/service/DoiServiceTest.kt | 13 ++++ .../src/test/resources/ExpectedDOIRequest.xml | 4 +- 22 files changed, 144 insertions(+), 68 deletions(-) diff --git a/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/constants/ExtSerializationFields.kt b/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/constants/ExtSerializationFields.kt index 66a8b3e859..544f2d02c4 100644 --- a/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/constants/ExtSerializationFields.kt +++ b/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/constants/ExtSerializationFields.kt @@ -38,6 +38,7 @@ object ExtSerializationFields { const val OWNER = "owner" const val SUBMITTER = "submitter" const val TITLE = "title" + const val DOI = "DOI" const val METHOD = "method" const val REL_PATH = "relPath" const val ROOT_PATH = "rootPath" diff --git a/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/serializers/ExtSubmissionSerializer.kt b/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/serializers/ExtSubmissionSerializer.kt index 514068d114..533f5f16a0 100644 --- a/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/serializers/ExtSubmissionSerializer.kt +++ b/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/serializers/ExtSubmissionSerializer.kt @@ -11,6 +11,7 @@ import uk.ac.ebi.extended.serialization.constants.ExtSerializationFields.ACC_NO import uk.ac.ebi.extended.serialization.constants.ExtSerializationFields.ATTRIBUTES import uk.ac.ebi.extended.serialization.constants.ExtSerializationFields.COLLECTIONS import uk.ac.ebi.extended.serialization.constants.ExtSerializationFields.CREATION_TIME +import uk.ac.ebi.extended.serialization.constants.ExtSerializationFields.DOI import uk.ac.ebi.extended.serialization.constants.ExtSerializationFields.METHOD import uk.ac.ebi.extended.serialization.constants.ExtSerializationFields.MOD_TIME import uk.ac.ebi.extended.serialization.constants.ExtSerializationFields.OWNER @@ -43,6 +44,7 @@ class ExtSubmissionSerializer : JsonSerializer() { gen.writeStringField(OWNER, submission.owner) gen.writeStringField(SUBMITTER, submission.submitter) gen.writeStringField(TITLE, submission.title) + gen.writeStringField(DOI, submission.doi) gen.writeStringField(METHOD, submission.method.name) gen.writeStringField(REL_PATH, submission.relPath) gen.writeStringField(ROOT_PATH, submission.rootPath) diff --git a/commons/commons-model-extended-serialization/src/testFixtures/kotlin/uk/ac/ebi/extended/test/SubmissionFactory.kt b/commons/commons-model-extended-serialization/src/testFixtures/kotlin/uk/ac/ebi/extended/test/SubmissionFactory.kt index c5a0c86926..968a3a9e00 100644 --- a/commons/commons-model-extended-serialization/src/testFixtures/kotlin/uk/ac/ebi/extended/test/SubmissionFactory.kt +++ b/commons/commons-model-extended-serialization/src/testFixtures/kotlin/uk/ac/ebi/extended/test/SubmissionFactory.kt @@ -52,6 +52,7 @@ object SubmissionFactory { owner = owner, submitter = submitter, title = title, + doi = null, method = method, relPath = relPath, rootPath = rootPath, @@ -69,24 +70,25 @@ object SubmissionFactory { ) const val ACC_NO = "S-TEST123" - const val VERSION = 1 - const val SCHEMA_VERSION = "1.0" - const val OWNER = "owner@email.org" - const val SUBMITTER = "submitter@email.org" - const val TITLE = "Default Submission Title" - val METHOD = ExtSubmissionMethod.PAGE_TAB - const val REL_PATH = "S-TEST/123/S-TEST123" - const val ROOT_PATH = "SUBMISSION_ROOT_PATH" - const val RELEASED = false - const val SECRET_KEY = "SUBMISSION_SECRET_KEY" - val RELEASE_TIME: OffsetDateTime = OffsetDateTime.of(2019, 9, 21, 0, 0, 0, 0, UTC) - val MODIFICATION_TIME: OffsetDateTime = OffsetDateTime.of(2020, 9, 21, 0, 0, 0, 0, UTC) - val CREATION_TIME: OffsetDateTime = OffsetDateTime.of(2018, 9, 21, 0, 0, 0, 0, UTC) - val ATTRIBUTES = emptyList() - val TAGS = emptyList() - val COLLECTIONS = emptyList() - val SECTION = defaultSection() - val PAGE_TAG_FILES = emptyList() + private const val VERSION = 1 + private const val SCHEMA_VERSION = "1.0" + private const val OWNER = "owner@email.org" + private const val SUBMITTER = "submitter@email.org" + private const val TITLE = "Default Submission Title" + private const val REL_PATH = "S-TEST/123/S-TEST123" + private const val ROOT_PATH = "SUBMISSION_ROOT_PATH" + private const val RELEASED = false + private const val SECRET_KEY = "SUBMISSION_SECRET_KEY" + + private val RELEASE_TIME: OffsetDateTime = OffsetDateTime.of(2019, 9, 21, 0, 0, 0, 0, UTC) + private val METHOD = ExtSubmissionMethod.PAGE_TAB + private val MODIFICATION_TIME: OffsetDateTime = OffsetDateTime.of(2020, 9, 21, 0, 0, 0, 0, UTC) + private val CREATION_TIME: OffsetDateTime = OffsetDateTime.of(2018, 9, 21, 0, 0, 0, 0, UTC) + private val ATTRIBUTES = emptyList() + private val TAGS = emptyList() + private val COLLECTIONS = emptyList() + private val SECTION = defaultSection() + private val PAGE_TAG_FILES = emptyList() } object SectionFactory { @@ -108,13 +110,13 @@ object SectionFactory { links = links, ) - const val ACC_NO = "accNo" - const val TYPE = "Study" - val FILE_LIST = null - val ATTRIBUTES = emptyList() - val SECTIONS = emptyList>() - val FILES = emptyList>() - val LINKS = emptyList>() + private const val ACC_NO = "accNo" + private const val TYPE = "Study" + private val FILE_LIST = null + private val ATTRIBUTES = emptyList() + private val SECTIONS = emptyList>() + private val FILES = emptyList>() + private val LINKS = emptyList>() } object FireFileFactory { @@ -138,13 +140,13 @@ object FireFileFactory { attributes = attributes ) - const val FILE_PATH = "folder/file.txt" - const val REL_PATH = "Files/folder/file.txt" - const val FIRE_ID = "fireId" - const val FIRE_PATH = "submission/Files/folder/file.txt" - const val MD5 = "md5" - const val SIZE = 1L - val ATTRIBUTES = emptyList() + private const val FILE_PATH = "folder/file.txt" + private const val REL_PATH = "Files/folder/file.txt" + private const val FIRE_ID = "fireId" + private const val FIRE_PATH = "submission/Files/folder/file.txt" + private const val MD5 = "md5" + private const val SIZE = 1L + private val ATTRIBUTES = emptyList() } object FileListFactory { @@ -161,9 +163,9 @@ object FileListFactory { ) const val FILE_PATH = "folder/fileList.txt" - val FILES = emptyList() - const val FILES_URL = "filesUrl" - val PAGE_TAG_FILES = emptyList() + private val FILES = emptyList() + private const val FILES_URL = "filesUrl" + private val PAGE_TAG_FILES = emptyList() } object AttributeFactory { @@ -176,7 +178,7 @@ object AttributeFactory { ) = ExtAttribute(name, value, reference, nameAttrs, valueAttrs) - const val NAME = "name" - const val VALUE = "value" - const val REFERENCE = false + private const val NAME = "name" + private const val VALUE = "value" + private const val REFERENCE = false } diff --git a/commons/commons-model-extended/src/main/kotlin/ebi/ac/uk/extended/model/ExtendedModel.kt b/commons/commons-model-extended/src/main/kotlin/ebi/ac/uk/extended/model/ExtendedModel.kt index 1db594a656..7769203da0 100644 --- a/commons/commons-model-extended/src/main/kotlin/ebi/ac/uk/extended/model/ExtendedModel.kt +++ b/commons/commons-model-extended/src/main/kotlin/ebi/ac/uk/extended/model/ExtendedModel.kt @@ -112,7 +112,6 @@ data class ExtSubmission( var schemaVersion: String, val submitter: String, val title: String?, - // TODO doi mappers val doi: String?, val method: ExtSubmissionMethod, val relPath: String, diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverter.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverter.kt index 7a2c14a1a8..546210a332 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverter.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverter.kt @@ -6,6 +6,7 @@ import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ACC_NO import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ATTRIBUTES import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_CREATION_TIME +import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_DOI import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ID import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_METHOD import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_MODIFICATION_TIME @@ -45,6 +46,7 @@ class DocSubmissionConverter( owner = source.getString(SUB_OWNER), submitter = source.getString(SUB_SUBMITTER), title = source.getString(SUB_TITLE), + doi = source.getString(SUB_DOI), method = DocSubmissionMethod.fromString(source.getString(SUB_METHOD)), relPath = source.getString(SUB_REL_PATH), rootPath = source.getString(SUB_ROOT_PATH), diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/shared/ConverterCommonsFields.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/shared/ConverterCommonsFields.kt index 2038334b2b..d6094a1e65 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/shared/ConverterCommonsFields.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/shared/ConverterCommonsFields.kt @@ -122,6 +122,7 @@ object DocSubmissionFields { const val SUB_OWNER = "owner" const val SUB_SUBMITTER = "submitter" const val SUB_TITLE = "title" + const val SUB_DOI = "doi" const val SUB_METHOD = "method" const val SUB_REL_PATH = "relPath" const val SUB_ROOT_PATH = "rootPath" diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverter.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverter.kt index 85fe25d05d..01ed44ad76 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverter.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverter.kt @@ -9,6 +9,7 @@ import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ACC_NO import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ATTRIBUTES import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_CREATION_TIME +import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_DOI import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ID import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_METHOD import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_MODIFICATION_TIME @@ -49,6 +50,7 @@ class SubmissionConverter( submissionDoc[SUB_OWNER] = submission.owner submissionDoc[SUB_SUBMITTER] = submission.submitter submissionDoc[SUB_TITLE] = submission.title + submissionDoc[SUB_DOI] = submission.doi submissionDoc[SUB_METHOD] = submission.method.value submissionDoc[SUB_REL_PATH] = submission.relPath submissionDoc[SUB_ROOT_PATH] = submission.rootPath diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/mapping/from/ToDocSubmissionMapper.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/mapping/from/ToDocSubmissionMapper.kt index ae8966164b..a319e0fa7e 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/mapping/from/ToDocSubmissionMapper.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/mapping/from/ToDocSubmissionMapper.kt @@ -23,6 +23,7 @@ class ToDocSubmissionMapper(private val toDocSectionMapper: ToDocSectionMapper) id = submissionId, accNo = accNo, title = title, + doi = doi, method = getMethod(method), version = version, schemaVersion = schemaVersion, diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/mapping/to/ToExtSubmissionMapper.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/mapping/to/ToExtSubmissionMapper.kt index 342645dada..75fd4d33fb 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/mapping/to/ToExtSubmissionMapper.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/mapping/to/ToExtSubmissionMapper.kt @@ -19,6 +19,7 @@ class ToExtSubmissionMapper( owner = sub.owner, submitter = sub.submitter, title = sub.title, + doi = sub.doi, version = sub.version, schemaVersion = sub.schemaVersion, method = getMethod(sub.method), diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/migrations/DatabaseChangeLog.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/migrations/DatabaseChangeLog.kt index e7b7576ce9..c341c06051 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/migrations/DatabaseChangeLog.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/migrations/DatabaseChangeLog.kt @@ -6,6 +6,7 @@ import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSectionFields.SE import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ACC_NO import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ATTRIBUTES +import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_DOI import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_MODIFICATION_TIME import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_OWNER import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_RELEASED @@ -57,6 +58,7 @@ internal val CHANGE_LOG_CLASSES = listOf( ChangeLog007::class.java, ChangeLog008::class.java, ChangeLog009::class.java, + ChangeLog010::class.java, ) @ChangeLog @@ -239,3 +241,11 @@ class ChangeLog009 { } } } + +@ChangeLog +class ChangeLog010 { + @ChangeSet(order = "010", id = "DOI Field", author = "System") + fun changeSet010(template: MongockTemplate) { + template.updateMulti(Query(), Update().set(SUB_DOI, null), DocSubmission::class.java) + } +} diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/model/SubmissionModel.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/model/SubmissionModel.kt index 3a09d8adfb..8211f3bf64 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/model/SubmissionModel.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/model/SubmissionModel.kt @@ -27,6 +27,7 @@ data class DocSubmission( val owner: String, val submitter: String, val title: String?, + val doi: String?, val method: DocSubmissionMethod, val relPath: String, val rootPath: String?, diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverterTest.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverterTest.kt index aab0a02897..8c41b5254e 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverterTest.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverterTest.kt @@ -107,6 +107,7 @@ internal class SubmissionConverterTest( owner = submissionOwner, submitter = submissionSubmitter, title = submissionTitle, + doi = submissionDoi, method = DocSubmissionMethod.PAGE_TAB, relPath = submissionRelPath, rootPath = submissionRootPath, @@ -132,6 +133,7 @@ internal class SubmissionConverterTest( const val submissionOwner = "owner@mail.org" const val submissionSubmitter = "submitter@mail.org" const val submissionTitle = "TestSubmission" + const val submissionDoi = "TestDOI" const val submissionRelPath = "/a/rel/path" const val submissionRootPath = "/a/root/path" const val submissionReleased = false diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/migrations/DatabaseChangeLogTest.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/migrations/DatabaseChangeLogTest.kt index 7e52fc3534..37646dc927 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/migrations/DatabaseChangeLogTest.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/migrations/DatabaseChangeLogTest.kt @@ -10,6 +10,7 @@ import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSectionFields.SE import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ACC_NO import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ATTRIBUTES +import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_DOI import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_MODIFICATION_TIME import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_OWNER import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_RELEASED @@ -270,6 +271,26 @@ internal class DatabaseChangeLogTest( assertSubmissionFilesIndexes() } + @Test + fun `run migration 010`() { + val submission = testDocSubmission.copy(id = ObjectId()) + val submissionCollection = mongoTemplate.createCollection() + val collectionName = submissionCollection.namespace.collectionName + mongoTemplate.insert(submission, collectionName) + mongoTemplate.updateMulti(Query(), Update().unset(SUB_DOI), DocSubmission::class.java) + + val submissions = mongoTemplate.findAll(collectionName) + assertThat(submissions).hasSize(1) + assertThat(submissions.first().containsKey(SUB_DOI)).isFalse() + + runMigrations(ChangeLog010::class.java) + + val subAfterMigration = mongoTemplate.findAll(collectionName) + assertThat(subAfterMigration).hasSize(1) + assertThat(subAfterMigration.first().containsKey(SUB_DOI)).isTrue() + assertThat(subAfterMigration.first()[SUB_DOI]).isNull() + } + private fun runMigrations(clazz: Class<*>) { val runner = createMongockConfig(mongoTemplate, springContext, listOf(clazz)) runner.run(DefaultApplicationArguments()) diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/SubmissionTestHelper.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/SubmissionTestHelper.kt index b18e77d3d3..af1e7474a3 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/SubmissionTestHelper.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/SubmissionTestHelper.kt @@ -25,9 +25,8 @@ internal const val SECRET_KEY = "a-secret-key" internal const val TAG_NAME = "component" internal const val TAG_VALUE = "web" internal const val PROJECT_ACC_NO = "BioImages" -internal const val STAT_TYPE = "VIEWS" -internal const val STAT_VALUE = 123L internal const val REL_PATH = "S-TEST/123/S-TEST123" +internal const val DOI = "10.6019/S-TEST123" val fireDocFile = FireDocFile("filename", "filePath", "relPath", "fireId", listOf(), "md5", 1L, FILE.value) val fireDocDirectory = FireDocFile("filename", "filePath", "relPath", "dirFireId", listOf(), "md5", 1L, DIR.value) @@ -45,6 +44,7 @@ object SubmissionTestHelper { owner = OWNER, submitter = SUBMITTER, title = SUB_TITLE, + doi = DOI, method = PAGE_TAB, relPath = REL_PATH, rootPath = ROOT_PATH, diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/SubmissionDocTestHelper.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/SubmissionDocTestHelper.kt index d9dd28829d..8def28a1bd 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/SubmissionDocTestHelper.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/SubmissionDocTestHelper.kt @@ -56,6 +56,7 @@ internal val testDocSubmission: DocSubmission owner = OWNER, submitter = SUBMITTER, title = SUB_TITLE, + doi = null, method = PAGE_TAB, relPath = REL_PATH, rootPath = ROOT_PATH, diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/ext/ExtSubmissionFactory.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/ext/ExtSubmissionFactory.kt index b6e91f9596..07bda7fa3a 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/ext/ExtSubmissionFactory.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/ext/ExtSubmissionFactory.kt @@ -22,6 +22,7 @@ const val SUBMISSION_REL_PATH = "/a/rel/path" const val SUBMISSION_ROOT_PATH = "/a/root/path" const val SUBMISSION_RELEASED = true const val SUBMISSION_SECRET_KEY = "a-secret-key" +const val SUBMISSION_DOI = "10.6019/S-TEST1" val RELEASE_TIME: OffsetDateTime = OffsetDateTime.of(2019, 9, 21, 10, 30, 34, 15, ZoneOffset.UTC) val MODIFICATION_TIME: OffsetDateTime = OffsetDateTime.of(2020, 9, 21, 10, 30, 34, 15, ZoneOffset.UTC) @@ -83,6 +84,7 @@ val fullExtSubmission = ExtSubmission( owner = SUBMISSION_OWNER, submitter = SUBMISSION_SUBMITTER, title = SUBMISSION_TITLE, + doi = SUBMISSION_DOI, method = SUBMISSION_METHOD, relPath = SUBMISSION_REL_PATH, rootPath = SUBMISSION_ROOT_PATH, diff --git a/submission/submission-webapp/src/main/kotlin/ac/uk/ebi/biostd/common/config/SubmitterConfig.kt b/submission/submission-webapp/src/main/kotlin/ac/uk/ebi/biostd/common/config/SubmitterConfig.kt index 8922198005..aa45b95ad0 100644 --- a/submission/submission-webapp/src/main/kotlin/ac/uk/ebi/biostd/common/config/SubmitterConfig.kt +++ b/submission/submission-webapp/src/main/kotlin/ac/uk/ebi/biostd/common/config/SubmitterConfig.kt @@ -181,13 +181,11 @@ class SubmitterConfig( @Bean fun submissionSubmitter( - doiService: DoiService, extSubmissionSubmitter: ExtSubmissionSubmitter, submissionProcessor: SubmissionProcessor, collectionValidationService: CollectionValidationService, draftService: SubmissionDraftPersistenceService, ): SubmissionSubmitter = SubmissionSubmitter( - doiService, extSubmissionSubmitter, submissionProcessor, collectionValidationService, @@ -196,6 +194,7 @@ class SubmitterConfig( @Bean fun submissionProcessor( + doiService: DoiService, persistenceService: SubmissionPersistenceService, timesService: TimesService, accNoService: AccNoService, @@ -204,6 +203,7 @@ class SubmitterConfig( toExtSectionMapper: ToExtSectionMapper, ): SubmissionProcessor = SubmissionProcessor( + doiService, persistenceService, timesService, accNoService, diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt index aaf1d3ec61..ede67a6160 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt @@ -1,6 +1,7 @@ package ac.uk.ebi.biostd.submission.model import ebi.ac.uk.util.collections.ifNotEmpty +import org.redundent.kotlin.xml.PrintOptions import org.redundent.kotlin.xml.xml import java.time.Instant @@ -23,7 +24,10 @@ internal class DoiRequest( fun asXmlRequest(): String { val timestamp = Instant.now().epochSecond.toString() return xml("doi_batch") { - xmlns = "http://www.crossref.org/schema/4.4.1" + xmlns = XML_NAMESPACE + attribute("xmlns:xsi", XML_SCHEMA_INSTANCE) + attribute("version", XML_SCHEMA_VERSION) + attribute("xsi:schemaLocation", "$XML_SCHEMA_LOCATION_1 $XML_SCHEMA_LOCATION_2") "head" { "doi_batch_id" { -timestamp } "timestamp" { -timestamp } @@ -44,10 +48,10 @@ internal class DoiRequest( "dataset" { contributors.ifNotEmpty { "contributors" { - contributors.forEachIndexed { index, contributor -> + contributors.forEach { contributor -> "person_name" { attribute("contributor_role", "author") - attribute("sequence", index) + attribute("sequence", "first") "given_name" { -contributor.name } "surname" { -contributor.surname } "affiliation" { -contributor.affiliation } @@ -71,7 +75,7 @@ internal class DoiRequest( } } } - }.toString() + }.toString(PrintOptions(pretty = true, singleLineTextElements = true, indent = " ")) } companion object { @@ -79,5 +83,10 @@ internal class DoiRequest( const val BS_TITLE = "BioStudies Database" const val DEPOSITOR = "EMBL-EBI" const val EMAIL = "biostudies@ebi.ac.uk" + const val XML_NAMESPACE = "http://www.crossref.org/schema/4.4.1" + const val XML_SCHEMA_VERSION = "4.4.1" + const val XML_SCHEMA_INSTANCE = "http://www.w3.org/2001/XMLSchema-instance" + const val XML_SCHEMA_LOCATION_1 = "http://www.crossref.org/schema/4.4.1" + const val XML_SCHEMA_LOCATION_2 = "http://www.crossref.org/schema/deposit/crossref4.4.1.xsd" } } diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt index 89f13bea1b..94239fca3c 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt @@ -23,24 +23,12 @@ import ebi.ac.uk.model.extensions.title import ebi.ac.uk.util.collections.ifNotEmpty import mu.KotlinLogging import org.springframework.core.io.FileSystemResource +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType.MULTIPART_FORM_DATA import org.springframework.util.LinkedMultiValueMap import org.springframework.web.reactive.function.client.WebClient import java.nio.file.Files -internal const val AFFILIATION_ATTR = "affiliation" -internal const val NAME_ATTR = "name" -internal const val ORCID_ATTR = "orcid" - -internal const val ORG_TYPE = "organization" -internal const val AUTHOR_TYPE = "author" - -internal const val FILE_PARAM = "fname" -internal const val OPERATION_PARAM = "operation" -internal const val OPERATION_PARAM_VALUE = "doMDUpload" -internal const val PASSWORD_PARAM = "login_password" -internal const val USER_PARAM = "login_id" -internal const val TEMP_FILE_NAME = "doi-request" - private val logger = KotlinLogging.logger {} @Suppress("ThrowsCount") @@ -49,12 +37,12 @@ class DoiService( private val properties: DoiProperties, ) { fun calculateDoi(accNo: String, rqt: SubmitRequest): String? { - val doi = rqt.submission.find(DOI) ?: return null + val doi = rqt.submission.attributes.find { it.name == DOI.name } ?: return null val previousDoi = rqt.previousVersion?.doi if (previousDoi != null) { - require(doi == previousDoi) { throw InvalidDoiException() } - return doi + require(doi.value == previousDoi) { throw InvalidDoiException() } + return previousDoi } return registerDoi(accNo, rqt) @@ -67,6 +55,7 @@ class DoiService( val requestFile = Files.createTempFile("${TEMP_FILE_NAME}_${accNo}", ".xml").toFile() FileUtils.writeContent(requestFile, request.asXmlRequest()) + val headers = HttpHeaders().apply { contentType = MULTIPART_FORM_DATA } val body = LinkedMultiValueMap().apply { add(USER_PARAM, properties.user) add(PASSWORD_PARAM, properties.password) @@ -74,8 +63,8 @@ class DoiService( add(FILE_PARAM, FileSystemResource(requestFile)) } - logger.info { "$accNo ${rqt.owner} Registering DOI" } - webClient.post(properties.endpoint, RequestParams(body = body)) + webClient.post(properties.endpoint, RequestParams(headers, body)) + logger.info { "$accNo ${rqt.owner} Registered DOI: '${request.doi}'" } return request.doi } @@ -117,4 +106,20 @@ class DoiService( return organizations } + + companion object { + internal const val AFFILIATION_ATTR = "affiliation" + internal const val NAME_ATTR = "name" + internal const val ORCID_ATTR = "orcid" + + internal const val ORG_TYPE = "organization" + internal const val AUTHOR_TYPE = "author" + + internal const val FILE_PARAM = "fname" + internal const val OPERATION_PARAM = "operation" + internal const val OPERATION_PARAM_VALUE = "doMDUpload" + internal const val PASSWORD_PARAM = "login_password" + internal const val USER_PARAM = "login_id" + internal const val TEMP_FILE_NAME = "doi-request" + } } diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt index e5315b3a26..f5ba0cd937 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt @@ -71,6 +71,7 @@ class SubmissionSubmitter( try { logger.info { "${rqt.accNo} ${rqt.owner} Started processing submission request" } + // TODO doi field isn't being persisted rqt.draftKey?.let { startProcessingDraft(rqt.accNo, rqt.owner, it) } val processed = submissionProcessor.processSubmission(rqt) collectionValidationService.executeCollectionValidators(processed) diff --git a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt index 8e6a0fc331..dae304f31d 100644 --- a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt +++ b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt @@ -11,10 +11,16 @@ import ac.uk.ebi.biostd.submission.exceptions.MissingDoiFieldException import ac.uk.ebi.biostd.submission.exceptions.MissingTitleException import ac.uk.ebi.biostd.submission.model.DoiRequest.Companion.BS_DOI_ID import ac.uk.ebi.biostd.submission.model.SubmitRequest +import ac.uk.ebi.biostd.submission.service.DoiService.Companion.FILE_PARAM +import ac.uk.ebi.biostd.submission.service.DoiService.Companion.OPERATION_PARAM +import ac.uk.ebi.biostd.submission.service.DoiService.Companion.OPERATION_PARAM_VALUE +import ac.uk.ebi.biostd.submission.service.DoiService.Companion.PASSWORD_PARAM +import ac.uk.ebi.biostd.submission.service.DoiService.Companion.USER_PARAM import ebi.ac.uk.dsl.attribute import ebi.ac.uk.dsl.section import ebi.ac.uk.dsl.submission import ebi.ac.uk.extended.model.ExtSubmission +import ebi.ac.uk.model.constants.MULTIPART_FORM_DATA import ebi.ac.uk.model.extensions.title import io.mockk.clearAllMocks import io.mockk.every @@ -30,6 +36,8 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.springframework.core.io.FileSystemResource +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpHeaders.CONTENT_TYPE import org.springframework.util.LinkedMultiValueMap import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec @@ -38,6 +46,7 @@ import java.nio.file.Paths import java.time.Instant import java.time.OffsetDateTime import java.time.ZoneOffset.UTC +import java.util.function.Consumer @ExtendWith(MockKExtension::class) class DoiServiceTest( @@ -62,6 +71,7 @@ class DoiServiceTest( fun `doi registration`( @MockK requestSpec: RequestBodySpec, ) { + val headersSlot = slot>() val bodySlot = slot>() val submission = submission { title = "Test Submission" @@ -84,10 +94,12 @@ class DoiServiceTest( every { submitRequest.submission } returns submission every { webClient.post().uri(properties.endpoint) } returns requestSpec every { requestSpec.bodyValue(capture(bodySlot)) } returns requestSpec + every { requestSpec.headers(capture(headersSlot)) } returns requestSpec every { requestSpec.retrieve().bodyToMono(String::class.java).block() } returns "OK" val doi = testInstance.calculateDoi(TEST_ACC_NO, submitRequest) val body = bodySlot.captured + val headers = headersSlot.captured val requestFile = body[FILE_PARAM]!!.first() as FileSystemResource val expectedXml = Files.readString(Paths.get("src/test/resources/ExpectedDOIRequest.xml")) @@ -96,6 +108,7 @@ class DoiServiceTest( assertThat(body[USER_PARAM]!!.first()).isEqualTo(properties.user) assertThat(body[PASSWORD_PARAM]!!.first()).isEqualTo(properties.password) assertThat(body[OPERATION_PARAM]!!.first()).isEqualTo(OPERATION_PARAM_VALUE) + headers.andThen { assertThat(it[CONTENT_TYPE]!!.first()).isEqualTo(MULTIPART_FORM_DATA) } verify(exactly = 1) { webClient.post().uri(properties.endpoint) requestSpec.bodyValue(body) diff --git a/submission/submitter/src/test/resources/ExpectedDOIRequest.xml b/submission/submitter/src/test/resources/ExpectedDOIRequest.xml index 13cca2df33..e3b02a9a69 100644 --- a/submission/submitter/src/test/resources/ExpectedDOIRequest.xml +++ b/submission/submitter/src/test/resources/ExpectedDOIRequest.xml @@ -1,4 +1,4 @@ - + 1600683060 1600683060 @@ -17,7 +17,7 @@ - + John Doe EMBL From 0e3861ac7c57111292dbc492374eb0bf9c0385d4 Mon Sep 17 00:00:00 2001 From: Jhoan Munoz Date: Tue, 29 Aug 2023 16:43:03 +0100 Subject: [PATCH 5/7] Pivotal ID # 184977702: Assign DOIs Automatically - Fix serialization - Add iTest --- .../extended/mapping/to/ToSubmissionMapper.kt | 2 ++ .../constants/ExtSerializationFields.kt | 2 +- .../submit/SpecialSubmissionAttributesTest.kt | 30 +++++++++++++++++++ .../itestsInventory/itestsInventory.md | 1 + .../ebi/biostd/submission/model/DoiRequest.kt | 4 +-- .../submitter/SubmissionSubmitter.kt | 2 -- .../request/SubmissionRequestSaver.kt | 4 +-- 7 files changed, 38 insertions(+), 7 deletions(-) diff --git a/commons/commons-model-extended-mapping/src/main/kotlin/ebi/ac/uk/extended/mapping/to/ToSubmissionMapper.kt b/commons/commons-model-extended-mapping/src/main/kotlin/ebi/ac/uk/extended/mapping/to/ToSubmissionMapper.kt index bf07da4765..462c99a832 100644 --- a/commons/commons-model-extended-mapping/src/main/kotlin/ebi/ac/uk/extended/mapping/to/ToSubmissionMapper.kt +++ b/commons/commons-model-extended-mapping/src/main/kotlin/ebi/ac/uk/extended/mapping/to/ToSubmissionMapper.kt @@ -5,6 +5,7 @@ import ebi.ac.uk.model.Attribute import ebi.ac.uk.model.Submission import ebi.ac.uk.model.constants.SubFields.ATTACH_TO import ebi.ac.uk.model.constants.SubFields.COLLECTION_VALIDATOR +import ebi.ac.uk.model.constants.SubFields.DOI import ebi.ac.uk.model.constants.SubFields.PUBLIC_ACCESS_TAG import ebi.ac.uk.model.constants.SubFields.RELEASE_DATE import ebi.ac.uk.model.constants.SubFields.ROOT_PATH @@ -25,6 +26,7 @@ class ToSubmissionMapper(private val toSectionMapper: ToSectionMapper) { private fun ExtSubmission.simpleAttributes(): List = buildSet { addAll(attributes.filter { it.name != COLLECTION_VALIDATOR.value }.map { it.toAttribute() }) title?.let { add(Attribute(TITLE.value, it)) } + doi?.let { add(Attribute(DOI.value, it)) } releaseTime?.let { add(Attribute(RELEASE_DATE.value, it.toLocalDate().toString())) } rootPath?.let { add(Attribute(ROOT_PATH.value, it)) } addAll(collections.filter { it.accNo != PUBLIC_ACCESS_TAG.value }.map { Attribute(ATTACH_TO.value, it.accNo) }) diff --git a/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/constants/ExtSerializationFields.kt b/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/constants/ExtSerializationFields.kt index 544f2d02c4..f3b0b66341 100644 --- a/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/constants/ExtSerializationFields.kt +++ b/commons/commons-model-extended-serialization/src/main/kotlin/uk/ac/ebi/extended/serialization/constants/ExtSerializationFields.kt @@ -38,7 +38,7 @@ object ExtSerializationFields { const val OWNER = "owner" const val SUBMITTER = "submitter" const val TITLE = "title" - const val DOI = "DOI" + const val DOI = "doi" const val METHOD = "method" const val REL_PATH = "relPath" const val ROOT_PATH = "rootPath" diff --git a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SpecialSubmissionAttributesTest.kt b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SpecialSubmissionAttributesTest.kt index 0fca3910b8..97f17d674f 100644 --- a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SpecialSubmissionAttributesTest.kt +++ b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SpecialSubmissionAttributesTest.kt @@ -11,6 +11,7 @@ import ac.uk.ebi.biostd.itest.itest.getWebClient import ac.uk.ebi.biostd.persistence.common.service.SubmissionPersistenceQueryService import ac.uk.ebi.biostd.persistence.model.DbTag import ac.uk.ebi.biostd.persistence.repositories.TagDataRepository +import ac.uk.ebi.biostd.submission.model.DoiRequest.Companion.BS_DOI_ID import arrow.core.Either import ebi.ac.uk.asserts.assertThat import ebi.ac.uk.dsl.section @@ -333,4 +334,33 @@ class SpecialSubmissionAttributesTest( assertFiles(section.files, fileName) assertSubSections(section.sections) } + + @Test + fun `15-6 submission with DOI`() { + val submission = tsv { + line("Submission", "S-STBL125") + line("Title", "Submission with DOI") + line("DOI") + + line("Study", "SECT-001") + line() + + line("Author") + line("Name", "Jane Doe") + line("ORCID", "1234-5678-9101-1121") + line("Affiliation", "o1") + line() + + line("Organization", "o1") + line("Name", "EMBL") + line() + }.toString() + + assertThat(webClient.submitSingle(submission, TSV)).isSuccessful() + + val savedSubmission = submissionRepository.getExtByAccNo("S-STBL125") + assertThat(savedSubmission.accNo).isEqualTo("S-STBL125") + assertThat(savedSubmission.title).isEqualTo("Submission with DOI") + assertThat(savedSubmission.doi).isEqualTo("$BS_DOI_ID/S-STBL125") + } } diff --git a/submission/submission-webapp/src/itest/resources/itestsInventory/itestsInventory.md b/submission/submission-webapp/src/itest/resources/itestsInventory/itestsInventory.md index 87fe11e33a..c04ab400e7 100644 --- a/submission/submission-webapp/src/itest/resources/itestsInventory/itestsInventory.md +++ b/submission/submission-webapp/src/itest/resources/itestsInventory/itestsInventory.md @@ -92,6 +92,7 @@ | SpecialSubmissionAttributesTest | 15-3 | new submission with empty accNo subsection table | | | SpecialSubmissionAttributesTest | 15-4 | new submission with empty-null attributes | | | SpecialSubmissionAttributesTest | 15-5 | new submission with empty-null table attributes | | +| SpecialSubmissionAttributesTest | 15-6 | submission with DOI | | | SubmissionApiTest | 16-1 | submit with submission object | Performs different simple submission cases, no accNo, invalid link url, rootPath, and others. | | SubmissionApiTest | 16-2 | empty accNo | | | SubmissionApiTest | 16-3 | submission with root path | | diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt index ede67a6160..6e46f4d137 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt @@ -5,14 +5,14 @@ import org.redundent.kotlin.xml.PrintOptions import org.redundent.kotlin.xml.xml import java.time.Instant -internal data class Contributor( +data class Contributor( val name: String, val surname: String, val affiliation: String, val orcid: String? ) -internal class DoiRequest( +class DoiRequest( private val accNo: String, private val title: String, private val instanceUrl: String, diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt index f5ba0cd937..63217ef636 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/SubmissionSubmitter.kt @@ -70,8 +70,6 @@ class SubmissionSubmitter( private suspend fun processRequest(rqt: SubmitRequest): ExtSubmission { try { logger.info { "${rqt.accNo} ${rqt.owner} Started processing submission request" } - - // TODO doi field isn't being persisted rqt.draftKey?.let { startProcessingDraft(rqt.accNo, rqt.owner, it) } val processed = submissionProcessor.processSubmission(rqt) collectionValidationService.executeCollectionValidators(processed) diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/request/SubmissionRequestSaver.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/request/SubmissionRequestSaver.kt index e88632615f..40bf0c4813 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/request/SubmissionRequestSaver.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/submitter/request/SubmissionRequestSaver.kt @@ -24,9 +24,9 @@ class SubmissionRequestSaver( val sub = request.submission logger.info { "$accNo ${sub.owner} Started saving submission '${sub.accNo}', version={${sub.version}}" } - val assemble = assembleSubmission(sub) + val assembled = assembleSubmission(sub) persistenceService.expirePreviousVersions(sub.accNo) - val saved = persistenceService.saveSubmission(assemble) + val saved = persistenceService.saveSubmission(assembled) logger.info { "$accNo ${sub.owner} Finished saving submission '${sub.accNo}', version={${sub.version}}" } requestService.saveSubmissionRequest(request.withNewStatus(PERSISTED)) From 0717878e8d1f823604e42826661a4bc88aa01360 Mon Sep 17 00:00:00 2001 From: Jhoan Munoz Date: Wed, 30 Aug 2023 18:05:59 +0100 Subject: [PATCH 6/7] Pivotal ID # 184977702: Assign DOIs Automatically - Finish mapping - Solve warnings - Remove unused code --- .../ebi/ac/uk/model/extensions/SubExt.kt | 9 ++ .../ebi/ac/uk/model/extensions/SubExtTest.kt | 9 ++ .../mapping/to/ToSubmissionMapperTest.kt | 13 +- .../ExtSubmissionSerializerTest.kt | 2 + .../service/ExtSerializationServiceExtTest.kt | 2 +- .../service/ExtSerializationServiceTest.kt | 3 +- .../ac/ebi/extended/test/SubmissionFactory.kt | 127 +++--------------- .../model/ExtSubmissionExtensionsTest.kt | 1 + .../service/RtNotificationServiceTest.kt | 1 + .../filesystem/ExtSubmissionFactory.kt | 55 -------- .../converters/from/DocSubmissionConverter.kt | 4 +- .../shared/ConverterCommonsFields.kt | 4 +- .../db/converters/to/SubmissionConverter.kt | 8 +- .../SubmissionRequestDocDataRepository.kt | 3 +- .../from/DocSubmissionConverterTest.kt | 101 +++++++------- .../converters/to/SubmissionConverterTest.kt | 88 ++++++------ .../data/SubmissionStatsDataRepositoryTest.kt | 2 + .../service/ExtSubmissionRepositoryTest.kt | 12 +- .../doc/service/StatsMongoDataServiceTest.kt | 2 + .../doc/test/doc/SubmissionDocTestHelper.kt | 3 +- .../itest/test/stats/SubmissionStatsTest.kt | 2 + .../submit/FileListValidationTest.kt | 2 + .../service/SubmissionStatsServiceTest.kt | 2 + .../service/SubmissionStagesHandlerTest.kt | 2 + .../biostd/submission/service/DoiService.kt | 4 +- 25 files changed, 172 insertions(+), 289 deletions(-) delete mode 100644 submission/persistence-filesystem/src/test/kotlin/ac/uk/ebi/biostd/persistence/filesystem/ExtSubmissionFactory.kt diff --git a/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/extensions/SubExt.kt b/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/extensions/SubExt.kt index b9aa269aac..d18b8e5cc2 100644 --- a/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/extensions/SubExt.kt +++ b/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/extensions/SubExt.kt @@ -88,6 +88,15 @@ var Submission.title: String? value?.let { this[SubFields.TITLE] = it } } +/** + * Obtain the submission title attribute if present. + */ +var Submission.doi: String? + get() = find(SubFields.DOI) + set(value) { + value?.let { this[SubFields.DOI] = it } + } + /** * Obtain the submission accession number template if present. */ diff --git a/commons/commons-bio/src/test/kotlin/ebi/ac/uk/model/extensions/SubExtTest.kt b/commons/commons-bio/src/test/kotlin/ebi/ac/uk/model/extensions/SubExtTest.kt index 181f0a50cf..8d4763cd97 100644 --- a/commons/commons-bio/src/test/kotlin/ebi/ac/uk/model/extensions/SubExtTest.kt +++ b/commons/commons-bio/src/test/kotlin/ebi/ac/uk/model/extensions/SubExtTest.kt @@ -92,6 +92,15 @@ class SubExtTest { assertExtendedAttribute(submission, SubFields.TITLE, "Title") } + @Test + fun doi() { + val submission = submission("ABC-123") {} + submission.doi = "10.6019/ABC-123" + + assertThat(submission.doi).isEqualTo("10.6019/ABC-123") + assertExtendedAttribute(submission, SubFields.DOI, "10.6019/ABC-123") + } + @Test fun `root path`() { val submission = submission("ABC-123") {} diff --git a/commons/commons-model-extended-mapping/src/test/kotlin/ebi/ac/uk/extended/mapping/to/ToSubmissionMapperTest.kt b/commons/commons-model-extended-mapping/src/test/kotlin/ebi/ac/uk/extended/mapping/to/ToSubmissionMapperTest.kt index 5b77e70f2c..84922e21ac 100644 --- a/commons/commons-model-extended-mapping/src/test/kotlin/ebi/ac/uk/extended/mapping/to/ToSubmissionMapperTest.kt +++ b/commons/commons-model-extended-mapping/src/test/kotlin/ebi/ac/uk/extended/mapping/to/ToSubmissionMapperTest.kt @@ -6,6 +6,7 @@ import ebi.ac.uk.model.Attribute import ebi.ac.uk.model.Section import ebi.ac.uk.model.Submission import ebi.ac.uk.model.extensions.attachTo +import ebi.ac.uk.model.extensions.doi import ebi.ac.uk.model.extensions.releaseDate import ebi.ac.uk.model.extensions.rootPath import ebi.ac.uk.model.extensions.title @@ -24,6 +25,7 @@ class ToSubmissionMapperTest( @MockK val toSectionMapper: ToSectionMapper, ) { private val extSubmission = basicExtSubmission.copy( + doi = "10.983/S-TEST123", rootPath = "/a/root/path", collections = listOf(ExtCollection("BioImages")), releaseTime = OffsetDateTime.of(2019, 9, 21, 0, 0, 0, 0, UTC), @@ -46,18 +48,11 @@ class ToSubmissionMapperTest( assertSubmissionAttributes(submission) } - private fun assertSection(submission: Submission) { - assertThat(submission.section.type).isEqualTo("Study") - assertThat(submission.section.attributes).isEmpty() - assertThat(submission.section.files).isEmpty() - assertThat(submission.section.links).isEmpty() - assertThat(submission.section.sections).isEmpty() - } - private fun assertSubmissionAttributes(submission: Submission) { - assertThat(submission.attributes).hasSize(5) + assertThat(submission.attributes).hasSize(6) assertThat(submission.attributes).contains(Attribute("Type", "Experiment")) assertThat(submission.title).isEqualTo("Test Submission") + assertThat(submission.doi).isEqualTo("10.983/S-TEST123") assertThat(submission.attachTo).isEqualTo("BioImages") assertThat(submission.releaseDate).isEqualTo("2019-09-21") assertThat(submission.rootPath).isEqualTo("/a/root/path") diff --git a/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/serializers/ExtSubmissionSerializerTest.kt b/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/serializers/ExtSubmissionSerializerTest.kt index 73822f026d..6197f6cf33 100644 --- a/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/serializers/ExtSubmissionSerializerTest.kt +++ b/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/serializers/ExtSubmissionSerializerTest.kt @@ -73,6 +73,7 @@ class ExtSubmissionSerializerTest { "owner" to "owner@mail.org" "submitter" to "submitter@mail.org" "title" to "Test Submission" + "doi" to "10.983/S-TEST1" "method" to ExtSubmissionMethod.PAGE_TAB "relPath" to "/a/rel/path" "rootPath" to "/a/root/path" @@ -142,6 +143,7 @@ class ExtSubmissionSerializerTest { owner = "owner@mail.org", submitter = "submitter@mail.org", title = "TestSubmission", + doi = "10.983/S-TEST1", method = ExtSubmissionMethod.PAGE_TAB, relPath = "/a/rel/path", rootPath = "/a/root/path", diff --git a/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/service/ExtSerializationServiceExtTest.kt b/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/service/ExtSerializationServiceExtTest.kt index be25e5b779..46e2e10f0e 100644 --- a/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/service/ExtSerializationServiceExtTest.kt +++ b/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/service/ExtSerializationServiceExtTest.kt @@ -25,7 +25,6 @@ import java.time.OffsetDateTime internal class ExtSerializationServiceExtTest( private val tmpFolder: TemporaryFolder, ) { - private val testInstance: ExtSerializationService = ExtSerializationService() @Test @@ -73,6 +72,7 @@ internal class ExtSerializationServiceExtTest( owner = "owner@mail.org", submitter = "submitter@mail.org", title = "TestSubmission", + doi = "10.983/S-TEST1", method = ExtSubmissionMethod.PAGE_TAB, relPath = "/a/rel/path", rootPath = "/a/root/path", diff --git a/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/service/ExtSerializationServiceTest.kt b/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/service/ExtSerializationServiceTest.kt index e3b87f5d13..0ccc793bc1 100644 --- a/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/service/ExtSerializationServiceTest.kt +++ b/commons/commons-model-extended-serialization/src/test/kotlin/uk/ac/ebi/extended/serialization/service/ExtSerializationServiceTest.kt @@ -15,7 +15,6 @@ import io.github.glytching.junit.extension.folder.TemporaryFolderExtension import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith -import uk.ac.ebi.extended.test.AttributeFactory.defaultAttribute import java.time.OffsetDateTime import java.time.ZoneOffset @@ -24,7 +23,6 @@ class ExtSerializationServiceTest(private val tempFolder: TemporaryFolder) { private val testInstance = ExtSerializationService() private val testFile = tempFolder.createFile("results.txt") private val nfsFile = tempFolder.createFile("file.txt") - private val attributes = (1..4).map { defaultAttribute(name = "name$it") } @Test fun `serialize - deserialize`() { @@ -37,6 +35,7 @@ class ExtSerializationServiceTest(private val tempFolder: TemporaryFolder) { owner = "owner@mail.org", submitter = "submitter@mail.org", title = "Test Submission", + doi = "10.983/S-TEST123", method = PAGE_TAB, relPath = "/a/rel/path", rootPath = "/a/root/path", diff --git a/commons/commons-model-extended-serialization/src/testFixtures/kotlin/uk/ac/ebi/extended/test/SubmissionFactory.kt b/commons/commons-model-extended-serialization/src/testFixtures/kotlin/uk/ac/ebi/extended/test/SubmissionFactory.kt index 968a3a9e00..6c3f031cb8 100644 --- a/commons/commons-model-extended-serialization/src/testFixtures/kotlin/uk/ac/ebi/extended/test/SubmissionFactory.kt +++ b/commons/commons-model-extended-serialization/src/testFixtures/kotlin/uk/ac/ebi/extended/test/SubmissionFactory.kt @@ -1,11 +1,9 @@ -@file:Suppress("LongParameterList", "MagicNumber") +@file:Suppress("LongParameterList") package uk.ac.ebi.extended.test import arrow.core.Either import ebi.ac.uk.extended.model.ExtAttribute -import ebi.ac.uk.extended.model.ExtAttributeDetail -import ebi.ac.uk.extended.model.ExtCollection import ebi.ac.uk.extended.model.ExtFile import ebi.ac.uk.extended.model.ExtFileList import ebi.ac.uk.extended.model.ExtFileTable @@ -14,82 +12,8 @@ import ebi.ac.uk.extended.model.ExtLink import ebi.ac.uk.extended.model.ExtLinkTable import ebi.ac.uk.extended.model.ExtSection import ebi.ac.uk.extended.model.ExtSectionTable -import ebi.ac.uk.extended.model.ExtSubmission -import ebi.ac.uk.extended.model.ExtSubmissionMethod -import ebi.ac.uk.extended.model.ExtTag import ebi.ac.uk.extended.model.FireFile -import ebi.ac.uk.extended.model.StorageMode import uk.ac.ebi.extended.serialization.service.createExtFileList -import uk.ac.ebi.extended.test.SectionFactory.defaultSection -import java.time.OffsetDateTime -import java.time.ZoneOffset.UTC - -object SubmissionFactory { - fun defaultSubmission( - accNo: String = ACC_NO, - version: Int = VERSION, - schemaVersion: String = SCHEMA_VERSION, - owner: String = OWNER, - submitter: String = SUBMITTER, - title: String? = TITLE, - method: ExtSubmissionMethod = METHOD, - relPath: String = REL_PATH, - rootPath: String? = ROOT_PATH, - released: Boolean = RELEASED, - secretKey: String = SECRET_KEY, - releaseTime: OffsetDateTime? = RELEASE_TIME, - modificationTime: OffsetDateTime = MODIFICATION_TIME, - creationTime: OffsetDateTime = CREATION_TIME, - section: ExtSection = SECTION, - attributes: List = ATTRIBUTES, - tags: List = TAGS, - collections: List = COLLECTIONS, - pageTabFiles: List = PAGE_TAG_FILES, - ) = ExtSubmission( - accNo = accNo, - version = version, - schemaVersion = schemaVersion, - owner = owner, - submitter = submitter, - title = title, - doi = null, - method = method, - relPath = relPath, - rootPath = rootPath, - released = released, - secretKey = secretKey, - releaseTime = releaseTime, - modificationTime = modificationTime, - creationTime = creationTime, - section = section, - attributes = attributes, - tags = tags, - collections = collections, - pageTabFiles = pageTabFiles, - storageMode = StorageMode.NFS - ) - - const val ACC_NO = "S-TEST123" - private const val VERSION = 1 - private const val SCHEMA_VERSION = "1.0" - private const val OWNER = "owner@email.org" - private const val SUBMITTER = "submitter@email.org" - private const val TITLE = "Default Submission Title" - private const val REL_PATH = "S-TEST/123/S-TEST123" - private const val ROOT_PATH = "SUBMISSION_ROOT_PATH" - private const val RELEASED = false - private const val SECRET_KEY = "SUBMISSION_SECRET_KEY" - - private val RELEASE_TIME: OffsetDateTime = OffsetDateTime.of(2019, 9, 21, 0, 0, 0, 0, UTC) - private val METHOD = ExtSubmissionMethod.PAGE_TAB - private val MODIFICATION_TIME: OffsetDateTime = OffsetDateTime.of(2020, 9, 21, 0, 0, 0, 0, UTC) - private val CREATION_TIME: OffsetDateTime = OffsetDateTime.of(2018, 9, 21, 0, 0, 0, 0, UTC) - private val ATTRIBUTES = emptyList() - private val TAGS = emptyList() - private val COLLECTIONS = emptyList() - private val SECTION = defaultSection() - private val PAGE_TAG_FILES = emptyList() -} object SectionFactory { fun defaultSection( @@ -110,13 +34,13 @@ object SectionFactory { links = links, ) - private const val ACC_NO = "accNo" - private const val TYPE = "Study" - private val FILE_LIST = null - private val ATTRIBUTES = emptyList() - private val SECTIONS = emptyList>() - private val FILES = emptyList>() - private val LINKS = emptyList>() + const val ACC_NO = "accNo" + const val TYPE = "Study" + val FILE_LIST = null + val ATTRIBUTES = emptyList() + val SECTIONS = emptyList>() + val FILES = emptyList>() + val LINKS = emptyList>() } object FireFileFactory { @@ -140,13 +64,13 @@ object FireFileFactory { attributes = attributes ) - private const val FILE_PATH = "folder/file.txt" - private const val REL_PATH = "Files/folder/file.txt" - private const val FIRE_ID = "fireId" - private const val FIRE_PATH = "submission/Files/folder/file.txt" - private const val MD5 = "md5" - private const val SIZE = 1L - private val ATTRIBUTES = emptyList() + const val FILE_PATH = "folder/file.txt" + const val REL_PATH = "Files/folder/file.txt" + const val FIRE_ID = "fireId" + const val FIRE_PATH = "submission/Files/folder/file.txt" + const val MD5 = "md5" + const val SIZE = 1L + val ATTRIBUTES = emptyList() } object FileListFactory { @@ -163,22 +87,7 @@ object FileListFactory { ) const val FILE_PATH = "folder/fileList.txt" - private val FILES = emptyList() - private const val FILES_URL = "filesUrl" - private val PAGE_TAG_FILES = emptyList() -} - -object AttributeFactory { - fun defaultAttribute( - name: String = NAME, - value: String = VALUE, - reference: Boolean = REFERENCE, - nameAttrs: List = listOf(), - valueAttrs: List = listOf(), - - ) = ExtAttribute(name, value, reference, nameAttrs, valueAttrs) - - private const val NAME = "name" - private const val VALUE = "value" - private const val REFERENCE = false + val FILES = emptyList() + const val FILES_URL = "filesUrl" + val PAGE_TAG_FILES = emptyList() } diff --git a/commons/commons-model-extended/src/test/kotlin/ebi/ac/uk/extended/model/ExtSubmissionExtensionsTest.kt b/commons/commons-model-extended/src/test/kotlin/ebi/ac/uk/extended/model/ExtSubmissionExtensionsTest.kt index f5a9b9c78d..99d5a76168 100644 --- a/commons/commons-model-extended/src/test/kotlin/ebi/ac/uk/extended/model/ExtSubmissionExtensionsTest.kt +++ b/commons/commons-model-extended/src/test/kotlin/ebi/ac/uk/extended/model/ExtSubmissionExtensionsTest.kt @@ -67,6 +67,7 @@ class ExtSubmissionExtensionsTest( storageMode = StorageMode.FIRE, submitter = "submitter@mail.org", title = subTitle, + doi = "10.983/S-TEST1", method = ExtSubmissionMethod.PAGE_TAB, relPath = "/a/rel/path", rootPath = null, diff --git a/submission/notifications/src/test/kotlin/ebi/ac/uk/notifications/service/RtNotificationServiceTest.kt b/submission/notifications/src/test/kotlin/ebi/ac/uk/notifications/service/RtNotificationServiceTest.kt index ea6807f757..9d5409a36d 100644 --- a/submission/notifications/src/test/kotlin/ebi/ac/uk/notifications/service/RtNotificationServiceTest.kt +++ b/submission/notifications/src/test/kotlin/ebi/ac/uk/notifications/service/RtNotificationServiceTest.kt @@ -309,6 +309,7 @@ internal class RtNotificationServiceTest( owner = "owner@mail.org", submitter = "submitter@mail.org", title = title, + doi = "10.983/S-TEST1", method = ExtSubmissionMethod.PAGE_TAB, relPath = "/a/rel/path", rootPath = "/a/root/path", diff --git a/submission/persistence-filesystem/src/test/kotlin/ac/uk/ebi/biostd/persistence/filesystem/ExtSubmissionFactory.kt b/submission/persistence-filesystem/src/test/kotlin/ac/uk/ebi/biostd/persistence/filesystem/ExtSubmissionFactory.kt deleted file mode 100644 index 7331018c23..0000000000 --- a/submission/persistence-filesystem/src/test/kotlin/ac/uk/ebi/biostd/persistence/filesystem/ExtSubmissionFactory.kt +++ /dev/null @@ -1,55 +0,0 @@ -package ac.uk.ebi.biostd.persistence.filesystem - -import arrow.core.Either.Companion.left -import ebi.ac.uk.extended.model.ExtFileList -import ebi.ac.uk.extended.model.ExtSection -import ebi.ac.uk.extended.model.ExtSubmission -import ebi.ac.uk.extended.model.ExtSubmissionMethod -import ebi.ac.uk.extended.model.StorageMode -import ebi.ac.uk.extended.model.createNfsFile -import uk.ac.ebi.extended.serialization.service.createExtFileList -import java.io.File -import java.time.OffsetDateTime -import java.time.ZoneOffset - -fun extSubmissionWithFileList(files: List, referencedFiles: List) = - ExtSubmission( - accNo = "ABC-123", - version = 1, - schemaVersion = "1.0", - storageMode = StorageMode.NFS, - title = "A Test Submission", - owner = "owner", - submitter = "submitter", - method = ExtSubmissionMethod.PAGE_TAB, - relPath = "ABC/ABCxxx123/ABC-123", - rootPath = null, - released = false, - secretKey = "a-secret-key", - releaseTime = null, - modificationTime = OffsetDateTime.of(2018, 10, 10, 0, 0, 0, 0, ZoneOffset.UTC), - creationTime = OffsetDateTime.of(2018, 10, 10, 0, 0, 0, 0, ZoneOffset.UTC), - attributes = emptyList(), - tags = emptyList(), - collections = emptyList(), - section = extSectionWithFileList(files, referencedFiles) - ) - -fun extSectionWithFileList(files: List, referencedFiles: List) = - ExtSection( - type = "Study", - files = files.map { left(createNfsFile(it.name, "relPath", it, emptyList())) }, - fileList = ExtFileList( - "fileList", - file = createExtFileList( - referencedFiles.map { - createNfsFile( - it.name, - "relPath", - it, - emptyList() - ) - } - ) - ) - ) diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverter.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverter.kt index 546210a332..088a23d63f 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverter.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverter.kt @@ -1,7 +1,7 @@ package ac.uk.ebi.biostd.persistence.doc.db.converters.from +import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.COLLECTION_DOC_ACC_NO import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.PAGE_TAB_FILES -import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.PROJECT_DOC_ACC_NO import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.STORAGE_MODE import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ACC_NO import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ATTRIBUTES @@ -66,5 +66,5 @@ class DocSubmissionConverter( private fun toDocTag(doc: Document): DocTag = DocTag(name = doc.getString(TAG_DOC_NAME), value = doc.getString(TAG_DOC_VALUE)) - private fun toDocCollection(doc: Document): DocCollection = DocCollection(accNo = doc.getString(PROJECT_DOC_ACC_NO)) + private fun toDocCollection(doc: Document) = DocCollection(accNo = doc.getString(COLLECTION_DOC_ACC_NO)) } diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/shared/ConverterCommonsFields.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/shared/ConverterCommonsFields.kt index d6094a1e65..bb6758376d 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/shared/ConverterCommonsFields.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/shared/ConverterCommonsFields.kt @@ -111,7 +111,7 @@ object DocSectionFields { object DocSubmissionFields { val DOC_SUBMISSION_CLASS: String = DocSubmission::class.java.canonicalName val DOC_TAG_CLASS: String = DocTag::class.java.canonicalName - val DOC_PROJECT_CLASS: String = DocCollection::class.java.canonicalName + val DOC_COLLECTION_CLASS: String = DocCollection::class.java.canonicalName const val CLASS_FIELD = "_class" const val SUB = "submission" @@ -138,7 +138,7 @@ object DocSubmissionFields { const val SUB_PROJECTS = "collections" const val TAG_DOC_NAME = "name" const val TAG_DOC_VALUE = "value" - const val PROJECT_DOC_ACC_NO = "accNo" + const val COLLECTION_DOC_ACC_NO = "accNo" const val SUB_STATS = "stats" const val PAGE_TAB_FILES = "pageTabFiles" const val STORAGE_MODE = "storageMode" diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverter.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverter.kt index 01ed44ad76..b8f1cbe43d 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverter.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverter.kt @@ -1,10 +1,10 @@ package ac.uk.ebi.biostd.persistence.doc.db.converters.to -import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.DOC_PROJECT_CLASS +import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.DOC_COLLECTION_CLASS import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.DOC_SUBMISSION_CLASS import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.DOC_TAG_CLASS import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.PAGE_TAB_FILES -import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.PROJECT_DOC_ACC_NO +import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.COLLECTION_DOC_ACC_NO import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.STORAGE_MODE import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ACC_NO import ac.uk.ebi.biostd.persistence.doc.db.converters.shared.DocSubmissionFields.SUB_ATTRIBUTES @@ -78,8 +78,8 @@ class SubmissionConverter( private fun collectionToDocument(docCollection: DocCollection): Document { val projectDoc = Document() - projectDoc[classField] = DOC_PROJECT_CLASS - projectDoc[PROJECT_DOC_ACC_NO] = docCollection.accNo + projectDoc[classField] = DOC_COLLECTION_CLASS + projectDoc[COLLECTION_DOC_ACC_NO] = docCollection.accNo return projectDoc } } diff --git a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/data/SubmissionRequestDocDataRepository.kt b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/data/SubmissionRequestDocDataRepository.kt index 8c8920e304..074ea9adb7 100644 --- a/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/data/SubmissionRequestDocDataRepository.kt +++ b/submission/persistence-mongo/src/main/kotlin/ac/uk/ebi/biostd/persistence/doc/db/data/SubmissionRequestDocDataRepository.kt @@ -64,7 +64,8 @@ class SubmissionRequestDocDataRepository( email: String? = null, ): Pair> { val query = Query().addCriteria(createQuery(filter, email)) - val requestCount = mongoTemplate.count(query, DocSubmissionRequest::class.java).block() + val requestCount = mongoTemplate.count(query, DocSubmissionRequest::class.java).block()!! + return when { requestCount <= filter.offset -> requestCount.toInt() to emptyList() else -> findActiveRequests(query, filter.offset, filter.limit) diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverterTest.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverterTest.kt index c1637cff98..163038846b 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverterTest.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/from/DocSubmissionConverterTest.kt @@ -48,25 +48,25 @@ internal class DocSubmissionConverterTest( private fun assertThatAll(result: DocSubmission) { assertThat(result).isInstanceOf(DocSubmission::class.java) assertThat(result.id).isEqualTo(subId) - assertThat(result.accNo).isEqualTo(subAccNo) - assertThat(result.version).isEqualTo(subVersion) - assertThat(result.schemaVersion).isEqualTo(subSchemaVersion) - assertThat(result.owner).isEqualTo(subOwner) - assertThat(result.submitter).isEqualTo(subSubmitter) - assertThat(result.title).isEqualTo(subTitle) - assertThat(result.method).isEqualTo(DocSubmissionMethod.fromString(subMethod)) - assertThat(result.relPath).isEqualTo(subRelPath) - assertThat(result.rootPath).isEqualTo(subRootPath) - assertThat(result.released).isEqualTo(subReleased) - assertThat(result.secretKey).isEqualTo(subSecretKey) + assertThat(result.accNo).isEqualTo(SUB_ACC_NO) + assertThat(result.version).isEqualTo(VERSION) + assertThat(result.schemaVersion).isEqualTo(SCHEMA_VERSION) + assertThat(result.owner).isEqualTo(OWNER) + assertThat(result.submitter).isEqualTo(SUBMITTER) + assertThat(result.title).isEqualTo(TITLE) + assertThat(result.method).isEqualTo(DocSubmissionMethod.fromString(METHOD)) + assertThat(result.relPath).isEqualTo(REL_PATH) + assertThat(result.rootPath).isEqualTo(ROOT_PATH) + assertThat(result.released).isEqualTo(RELEASED) + assertThat(result.secretKey).isEqualTo(SECRET_KEY) assertThat(result.releaseTime).isEqualTo(subReleaseTime.toInstant()) assertThat(result.modificationTime).isEqualTo(subModificationTime.toInstant()) assertThat(result.creationTime).isEqualTo(subCreationTime.toInstant()) assertThat(result.section).isEqualTo(docSection) assertThat(result.attributes).isEqualTo(listOf(docAttribute)) - assertThat(result.tags[0].name).isEqualTo(tagDocName) - assertThat(result.tags[0].value).isEqualTo(tagDocValue) - assertThat(result.collections[0].accNo).isEqualTo(projectDocAccNo) + assertThat(result.tags[0].name).isEqualTo(TAG_DOC_NAME) + assertThat(result.tags[0].value).isEqualTo(TAG_DOC_VALUE) + assertThat(result.collections[0].accNo).isEqualTo(COLLECTION_DOC_ACC_NO) assertThat(result.pageTabFiles).isEqualTo(listOf(docFile)) assertThat(result.storageMode).isEqualTo(StorageMode.NFS) } @@ -79,18 +79,18 @@ internal class DocSubmissionConverterTest( val subDocument = Document() subDocument[DocSubmissionFields.CLASS_FIELD] = docSubmissionClass subDocument[DocSubmissionFields.SUB_ID] = subId - subDocument[DocSubmissionFields.SUB_ACC_NO] = subAccNo - subDocument[DocSubmissionFields.SUB_VERSION] = subVersion - subDocument[DocSubmissionFields.SUB_SCHEMA_VERSION] = subSchemaVersion - subDocument[DocSubmissionFields.SUB_OWNER] = subOwner - subDocument[DocSubmissionFields.SUB_SUBMITTER] = subSubmitter - subDocument[DocSubmissionFields.SUB_TITLE] = subTitle - subDocument[DocSubmissionFields.SUB_METHOD] = subMethod - subDocument[DocSubmissionFields.SUB_REL_PATH] = subRelPath - subDocument[DocSubmissionFields.SUB_ROOT_PATH] = subRootPath - subDocument[DocSubmissionFields.SUB_RELEASED] = subReleased - subDocument[DocSubmissionFields.SUB_SECRET_KEY] = subSecretKey - subDocument[DocSubmissionFields.SUB_STATUS] = subStatus + subDocument[DocSubmissionFields.SUB_ACC_NO] = SUB_ACC_NO + subDocument[DocSubmissionFields.SUB_VERSION] = VERSION + subDocument[DocSubmissionFields.SUB_SCHEMA_VERSION] = SCHEMA_VERSION + subDocument[DocSubmissionFields.SUB_OWNER] = OWNER + subDocument[DocSubmissionFields.SUB_SUBMITTER] = SUBMITTER + subDocument[DocSubmissionFields.SUB_TITLE] = TITLE + subDocument[DocSubmissionFields.SUB_METHOD] = METHOD + subDocument[DocSubmissionFields.SUB_REL_PATH] = REL_PATH + subDocument[DocSubmissionFields.SUB_ROOT_PATH] = ROOT_PATH + subDocument[DocSubmissionFields.SUB_RELEASED] = RELEASED + subDocument[DocSubmissionFields.SUB_SECRET_KEY] = SECRET_KEY + subDocument[DocSubmissionFields.SUB_STATUS] = STATUS subDocument[DocSubmissionFields.SUB_RELEASE_TIME] = subReleaseTime subDocument[DocSubmissionFields.SUB_MODIFICATION_TIME] = subModificationTime subDocument[DocSubmissionFields.SUB_CREATION_TIME] = subCreationTime @@ -99,43 +99,44 @@ internal class DocSubmissionConverterTest( subDocument[DocSubmissionFields.SUB_TAGS] = listOf(createTagDocument()) subDocument[DocSubmissionFields.SUB_PROJECTS] = listOf(createProjectDocument()) subDocument[DocSubmissionFields.PAGE_TAB_FILES] = listOf(fileDocument) - subDocument[DocSubmissionFields.STORAGE_MODE] = storageMode + subDocument[DocSubmissionFields.STORAGE_MODE] = STORAGE_MODE return subDocument } private fun createTagDocument(): Document { val tagDoc = Document() - tagDoc[DocSubmissionFields.TAG_DOC_NAME] = tagDocName - tagDoc[DocSubmissionFields.TAG_DOC_VALUE] = tagDocValue + tagDoc[DocSubmissionFields.TAG_DOC_NAME] = TAG_DOC_NAME + tagDoc[DocSubmissionFields.TAG_DOC_VALUE] = TAG_DOC_VALUE return tagDoc } private fun createProjectDocument(): Document { val projectDoc = Document() - projectDoc[DocSubmissionFields.PROJECT_DOC_ACC_NO] = projectDocAccNo + projectDoc[DocSubmissionFields.COLLECTION_DOC_ACC_NO] = COLLECTION_DOC_ACC_NO return projectDoc } companion object { - val subId = ObjectId(1, 1) - const val subAccNo = "accNo" - const val projectDocAccNo = "accNo" - const val subVersion = 1 - const val subSchemaVersion = "1.0" - const val subOwner = "owner" - const val subSubmitter = "submitter" - const val subTitle = "title" - const val subMethod = "FILE" - const val subStatus = "PROCESSED" - const val subRelPath = "relPath" - const val subRootPath = "rootPath" - const val subReleased = false - const val subSecretKey = "secretKey" - val subReleaseTime: Date = Date(110) - val subModificationTime: Date = Date(220) - val subCreationTime: Date = Date(330) - const val tagDocName = "name" - const val tagDocValue = "value" - const val storageMode = "NFS" + private val subId = ObjectId(1, 1) + private val subReleaseTime: Date = Date(110) + private val subModificationTime: Date = Date(220) + private val subCreationTime: Date = Date(330) + + private const val SUB_ACC_NO = "accNo" + private const val COLLECTION_DOC_ACC_NO = "accNo" + private const val VERSION = 1 + private const val SCHEMA_VERSION = "1.0" + private const val OWNER = "owner" + private const val SUBMITTER = "submitter" + private const val TITLE = "title" + private const val METHOD = "FILE" + private const val STATUS = "PROCESSED" + private const val REL_PATH = "relPath" + private const val ROOT_PATH = "rootPath" + private const val RELEASED = false + private const val SECRET_KEY = "secretKey" + private const val TAG_DOC_NAME = "name" + private const val TAG_DOC_VALUE = "value" + private const val STORAGE_MODE = "NFS" } } diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverterTest.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverterTest.kt index 8c41b5254e..6639789313 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverterTest.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/converters/to/SubmissionConverterTest.kt @@ -65,17 +65,17 @@ internal class SubmissionConverterTest( val result = testInstance.convert(docSubmission) assertThat(result[SUB_ID]).isEqualTo(submissionId) - assertThat(result[SUB_ACC_NO]).isEqualTo(submissionAccNo) - assertThat(result[SUB_VERSION]).isEqualTo(submissionVersion) - assertThat(result[SUB_SCHEMA_VERSION]).isEqualTo(submissionSchemaVersion) - assertThat(result[SUB_OWNER]).isEqualTo(submissionOwner) - assertThat(result[SUB_SUBMITTER]).isEqualTo(submissionSubmitter) - assertThat(result[SUB_TITLE]).isEqualTo(submissionTitle) + assertThat(result[SUB_ACC_NO]).isEqualTo(ACC_NO) + assertThat(result[SUB_VERSION]).isEqualTo(VERSION) + assertThat(result[SUB_SCHEMA_VERSION]).isEqualTo(SCHEMA_VERSION) + assertThat(result[SUB_OWNER]).isEqualTo(OWNER) + assertThat(result[SUB_SUBMITTER]).isEqualTo(SUBMITTER) + assertThat(result[SUB_TITLE]).isEqualTo(TITLE) assertThat(result[SUB_METHOD]).isEqualTo(DocSubmissionMethod.PAGE_TAB.value) - assertThat(result[SUB_REL_PATH]).isEqualTo(submissionRelPath) - assertThat(result[SUB_ROOT_PATH]).isEqualTo(submissionRootPath) - assertThat(result[SUB_RELEASED]).isEqualTo(submissionReleased) - assertThat(result[SUB_SECRET_KEY]).isEqualTo(submissionSecretKey) + assertThat(result[SUB_REL_PATH]).isEqualTo(REL_PATH) + assertThat(result[SUB_ROOT_PATH]).isEqualTo(ROOT_PATH) + assertThat(result[SUB_RELEASED]).isEqualTo(RELEASED) + assertThat(result[SUB_SECRET_KEY]).isEqualTo(SECRET_KEY) assertThat(result[SUB_RELEASE_TIME]).isEqualTo(submissionReleaseTime) assertThat(result[SUB_MODIFICATION_TIME]).isEqualTo(submissionModificationTime) assertThat(result[SUB_CREATION_TIME]).isEqualTo(submissionCreationTime) @@ -86,12 +86,12 @@ internal class SubmissionConverterTest( val tags = result.getAs>(DocSubmissionFields.SUB_TAGS) val tag = tags.first() - assertThat(tag[DocSubmissionFields.TAG_DOC_NAME]).isEqualTo(docTagName) - assertThat(tag[DocSubmissionFields.TAG_DOC_VALUE]).isEqualTo(docTagValue) + assertThat(tag[DocSubmissionFields.TAG_DOC_NAME]).isEqualTo(TAG_NAME) + assertThat(tag[DocSubmissionFields.TAG_DOC_VALUE]).isEqualTo(TAG_VALUE) val projects = result.getAs>(DocSubmissionFields.SUB_PROJECTS) val project = projects.first() - assertThat(project[DocSubmissionFields.PROJECT_DOC_ACC_NO]).isEqualTo(docProjectAccNo) + assertThat(project[DocSubmissionFields.COLLECTION_DOC_ACC_NO]).isEqualTo(COLLECTION_ACC_NO) } private fun createDocSubmission( @@ -101,18 +101,18 @@ internal class SubmissionConverterTest( ): DocSubmission { return DocSubmission( id = submissionId, - accNo = submissionAccNo, - version = submissionVersion, - schemaVersion = submissionSchemaVersion, - owner = submissionOwner, - submitter = submissionSubmitter, - title = submissionTitle, - doi = submissionDoi, + accNo = ACC_NO, + version = VERSION, + schemaVersion = SCHEMA_VERSION, + owner = OWNER, + submitter = SUBMITTER, + title = TITLE, + doi = DOI, method = DocSubmissionMethod.PAGE_TAB, - relPath = submissionRelPath, - rootPath = submissionRootPath, - released = submissionReleased, - secretKey = submissionSecretKey, + relPath = REL_PATH, + rootPath = ROOT_PATH, + released = RELEASED, + secretKey = SECRET_KEY, releaseTime = submissionReleaseTime, modificationTime = submissionModificationTime, creationTime = submissionCreationTime, @@ -126,27 +126,27 @@ internal class SubmissionConverterTest( } private companion object { - val submissionId = ObjectId() - const val submissionAccNo = "S-TEST1" - const val submissionVersion = 1 - const val submissionSchemaVersion = "1.0" - const val submissionOwner = "owner@mail.org" - const val submissionSubmitter = "submitter@mail.org" - const val submissionTitle = "TestSubmission" - const val submissionDoi = "TestDOI" - const val submissionRelPath = "/a/rel/path" - const val submissionRootPath = "/a/root/path" - const val submissionReleased = false - const val submissionSecretKey = "a-secret-key" - val submissionReleaseTime: Instant = Instant.ofEpochSecond(1) - val submissionModificationTime: Instant = Instant.ofEpochSecond(2) - val submissionCreationTime: Instant = Instant.ofEpochSecond(3) + private val submissionId = ObjectId() + private const val ACC_NO = "S-TEST1" + private const val VERSION = 1 + private const val SCHEMA_VERSION = "1.0" + private const val OWNER = "owner@mail.org" + private const val SUBMITTER = "submitter@mail.org" + private const val TITLE = "TestSubmission" + private const val DOI = "10.983/S-TEST1" + private const val REL_PATH = "/a/rel/path" + private const val ROOT_PATH = "/a/root/path" + private const val RELEASED = false + private const val SECRET_KEY = "a-secret-key" + private val submissionReleaseTime: Instant = Instant.ofEpochSecond(1) + private val submissionModificationTime: Instant = Instant.ofEpochSecond(2) + private val submissionCreationTime: Instant = Instant.ofEpochSecond(3) - private const val docTagName = "component" - private const val docTagValue = "web" - val submissionTags = listOf(DocTag(docTagName, docTagValue)) + private const val TAG_NAME = "component" + private const val TAG_VALUE = "web" + private val submissionTags = listOf(DocTag(TAG_NAME, TAG_VALUE)) - private const val docProjectAccNo = "BioImages" - val submissionProjects = listOf(DocCollection(docProjectAccNo)) + private const val COLLECTION_ACC_NO = "BioImages" + private val submissionProjects = listOf(DocCollection(COLLECTION_ACC_NO)) } } diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/data/SubmissionStatsDataRepositoryTest.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/data/SubmissionStatsDataRepositoryTest.kt index c822c0a0e5..0f73463ed0 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/data/SubmissionStatsDataRepositoryTest.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/db/data/SubmissionStatsDataRepositoryTest.kt @@ -7,6 +7,7 @@ import ac.uk.ebi.biostd.persistence.doc.model.DocSubmissionStats import ac.uk.ebi.biostd.persistence.doc.model.SingleSubmissionStat import ebi.ac.uk.db.MINIMUM_RUNNING_TIME import ebi.ac.uk.db.MONGO_VERSION +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.reactor.awaitSingleOrNull import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat @@ -29,6 +30,7 @@ import java.time.Duration.ofSeconds @ExtendWith(SpringExtension::class) @Testcontainers @SpringBootTest(classes = [MongoDbReposConfig::class]) +@OptIn(ExperimentalCoroutinesApi::class) class SubmissionStatsDataRepositoryTest { @Autowired lateinit var testInstance: SubmissionStatsDataRepository diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/service/ExtSubmissionRepositoryTest.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/service/ExtSubmissionRepositoryTest.kt index eba1b391ee..ff50b9584a 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/service/ExtSubmissionRepositoryTest.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/service/ExtSubmissionRepositoryTest.kt @@ -14,6 +14,7 @@ import ac.uk.ebi.biostd.persistence.doc.test.SubmissionTestHelper.docSubmission import ac.uk.ebi.biostd.persistence.doc.test.beans.TestConfig import ebi.ac.uk.db.MINIMUM_RUNNING_TIME import ebi.ac.uk.db.MONGO_VERSION +import ebi.ac.uk.test.basicExtSubmission import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -31,11 +32,8 @@ import uk.ac.ebi.extended.test.FileListFactory.FILE_PATH import uk.ac.ebi.extended.test.FileListFactory.defaultFileList import uk.ac.ebi.extended.test.FireFileFactory.defaultFireFile import uk.ac.ebi.extended.test.SectionFactory.defaultSection -import uk.ac.ebi.extended.test.SubmissionFactory.ACC_NO -import uk.ac.ebi.extended.test.SubmissionFactory.defaultSubmission import uk.ac.ebi.serialization.common.FilesResolver import java.time.Duration -import kotlin.text.Typography.section @Testcontainers @SpringBootTest(classes = [MongoDbReposConfig::class, TestConfig::class]) @@ -66,7 +64,7 @@ class ExtSubmissionRepositoryTest( @Test fun `save submission`() { val section = defaultSection(fileList = defaultFileList(files = listOf(defaultFireFile()))) - val submission = defaultSubmission(section = section, version = 1) + val submission = basicExtSubmission.copy(section = section) val result = testInstance.saveSubmission(submission) @@ -91,12 +89,12 @@ class ExtSubmissionRepositoryTest( @Test fun `expire previous versions`() { - subDataRepository.save(docSubmission.copy(accNo = ACC_NO, version = 1)) + subDataRepository.save(docSubmission.copy(accNo = "S-TEST123", version = 1)) assertThat(subDataRepository.findAll()).hasSize(1) - testInstance.expirePreviousVersions(ACC_NO) + testInstance.expirePreviousVersions("S-TEST123") - assertThat(subDataRepository.getSubmission(ACC_NO, -1)).isNotNull() + assertThat(subDataRepository.getSubmission("S-TEST123", -1)).isNotNull() assertThat(subDataRepository.findAll()).hasSize(1) } diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/service/StatsMongoDataServiceTest.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/service/StatsMongoDataServiceTest.kt index 22cbf31058..8233437217 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/service/StatsMongoDataServiceTest.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/service/StatsMongoDataServiceTest.kt @@ -19,6 +19,7 @@ import ebi.ac.uk.util.collections.third import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat @@ -42,6 +43,7 @@ import java.time.Duration.ofSeconds @ExtendWith(MockKExtension::class, SpringExtension::class) @Testcontainers @SpringBootTest(classes = [MongoDbReposConfig::class]) +@OptIn(ExperimentalCoroutinesApi::class) class StatsMongoDataServiceTest( @MockK private val submissionsRepository: SubmissionMongoRepository, @Autowired private val submissionStatsDataRepository: SubmissionStatsDataRepository, diff --git a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/SubmissionDocTestHelper.kt b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/SubmissionDocTestHelper.kt index 8def28a1bd..056f9d4f03 100644 --- a/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/SubmissionDocTestHelper.kt +++ b/submission/persistence-mongo/src/test/kotlin/ac/uk/ebi/biostd/persistence/doc/test/doc/SubmissionDocTestHelper.kt @@ -39,6 +39,7 @@ internal const val TAG_VALUE = "web" internal const val COLLECTION_ACC_NO = "BioImages" internal const val STAT_VALUE = 123L internal const val REL_PATH = "S-TEST/123/S-TEST123" +internal const val DOI = "10.6019/S-TEST123" internal val CREATION_TIME = Instant.now() internal val MODIFICATION_TIME = CREATION_TIME.plus(1, ChronoUnit.HOURS) internal val RELEASE_TIME = CREATION_TIME.plus(1, ChronoUnit.DAYS) @@ -56,7 +57,7 @@ internal val testDocSubmission: DocSubmission owner = OWNER, submitter = SUBMITTER, title = SUB_TITLE, - doi = null, + doi = DOI, method = PAGE_TAB, relPath = REL_PATH, rootPath = ROOT_PATH, diff --git a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/stats/SubmissionStatsTest.kt b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/stats/SubmissionStatsTest.kt index 368d46812c..26f77d65ee 100644 --- a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/stats/SubmissionStatsTest.kt +++ b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/stats/SubmissionStatsTest.kt @@ -18,6 +18,7 @@ import ebi.ac.uk.extended.model.StorageMode.NFS import ebi.ac.uk.io.ext.createFile import ebi.ac.uk.model.SubmissionStat import ebi.ac.uk.util.date.toStringDate +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat @@ -36,6 +37,7 @@ import java.time.OffsetDateTime @Import(FilePersistenceConfig::class) @ExtendWith(SpringExtension::class) +@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class SubmissionStatsTest( @Autowired val statsDataService: StatsDataService, diff --git a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/FileListValidationTest.kt b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/FileListValidationTest.kt index 763c93f71a..a02e4b36e5 100644 --- a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/FileListValidationTest.kt +++ b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/FileListValidationTest.kt @@ -19,6 +19,7 @@ import ebi.ac.uk.dsl.tsv.tsv import ebi.ac.uk.io.ext.createFile import ebi.ac.uk.io.ext.md5 import ebi.ac.uk.io.ext.size +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertThrows @@ -38,6 +39,7 @@ import uk.ac.ebi.fire.client.integration.web.FireClient @Import(FilePersistenceConfig::class) @ExtendWith(SpringExtension::class) +@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class FileListValidationTest( @Autowired private val fireClient: FireClient, diff --git a/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/stats/domain/service/SubmissionStatsServiceTest.kt b/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/stats/domain/service/SubmissionStatsServiceTest.kt index ff664f4a22..8d79128174 100644 --- a/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/stats/domain/service/SubmissionStatsServiceTest.kt +++ b/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/stats/domain/service/SubmissionStatsServiceTest.kt @@ -18,6 +18,7 @@ import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension import io.mockk.mockkStatic import io.mockk.slot +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest @@ -31,6 +32,7 @@ import uk.ac.ebi.extended.serialization.service.fileSequence import java.io.File @ExtendWith(MockKExtension::class) +@OptIn(ExperimentalCoroutinesApi::class) class SubmissionStatsServiceTest( @MockK private val statsFileHandler: StatsFileHandler, @MockK private val tempFileGenerator: TempFileGenerator, diff --git a/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/submission/domain/service/SubmissionStagesHandlerTest.kt b/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/submission/domain/service/SubmissionStagesHandlerTest.kt index 6db3a2acee..4f81ff316b 100644 --- a/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/submission/domain/service/SubmissionStagesHandlerTest.kt +++ b/submission/submission-webapp/src/test/kotlin/ac/uk/ebi/biostd/submission/domain/service/SubmissionStagesHandlerTest.kt @@ -19,6 +19,7 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test @@ -26,6 +27,7 @@ import org.junit.jupiter.api.extension.ExtendWith import uk.ac.ebi.events.service.EventsPublisherService @ExtendWith(MockKExtension::class) +@OptIn(ExperimentalCoroutinesApi::class) class SubmissionStagesHandlerTest( @MockK private val statsService: SubmissionStatsService, @MockK private val submissionSubmitter: SubmissionSubmitter, diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt index 94239fca3c..3d20c18033 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt @@ -31,7 +31,7 @@ import java.nio.file.Files private val logger = KotlinLogging.logger {} -@Suppress("ThrowsCount") +@Suppress("ThrowsCount", "ReturnCount") class DoiService( private val webClient: WebClient, private val properties: DoiProperties, @@ -52,7 +52,7 @@ class DoiService( val sub = rqt.submission val title = requireNotNull(sub.title) { throw MissingTitleException() } val request = DoiRequest(accNo, title, properties.uiUrl, getContributors(sub)) - val requestFile = Files.createTempFile("${TEMP_FILE_NAME}_${accNo}", ".xml").toFile() + val requestFile = Files.createTempFile("${TEMP_FILE_NAME}_$accNo", ".xml").toFile() FileUtils.writeContent(requestFile, request.asXmlRequest()) val headers = HttpHeaders().apply { contentType = MULTIPART_FORM_DATA } From 929f59d026c1d43b3de41a8c23127c4e7bd0fd44 Mon Sep 17 00:00:00 2001 From: Jhoan Munoz Date: Thu, 14 Sep 2023 12:09:46 +0100 Subject: [PATCH 7/7] Pivotal ID # 184977702: Assign DOIs Automatically - Simplify DOI service code - Use common http methods - Fix warnings --- .../biostd/client/api/ExtSubmissionClient.kt | 2 +- .../uk/ebi/biostd/client/api/StatsClient.kt | 4 +- .../common/BioWebClientRequestBuilder.kt | 2 +- .../ebi/ac/uk/model/extensions/SubExt.kt | 2 +- .../commons/http/builder/HttpParamBuilders.kt | 2 +- .../ebi/biostd/itest/itest/ITestListener.kt | 67 +++++++------ .../submit/SubmissionFileSourceTest.kt | 2 + .../submit/SubmissionPerformanceTest.kt | 6 +- .../itest/wiremock/TestWireMockTransformer.kt | 2 +- .../submission/exceptions/DoiExceptions.kt | 11 ++- .../ebi/biostd/submission/model/DoiRequest.kt | 3 +- .../biostd/submission/service/DoiService.kt | 96 +++++++++++-------- .../submission/service/DoiServiceTest.kt | 92 +++++++++++++++--- .../src/test/resources/ExpectedDOIRequest.xml | 37 ------- 14 files changed, 193 insertions(+), 135 deletions(-) rename client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/extensions/Common.kt => commons/commons-http/src/main/kotlin/ebi/ac/uk/commons/http/builder/HttpParamBuilders.kt (94%) delete mode 100644 submission/submitter/src/test/resources/ExpectedDOIRequest.xml diff --git a/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/api/ExtSubmissionClient.kt b/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/api/ExtSubmissionClient.kt index e55e33dc75..d844678e78 100644 --- a/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/api/ExtSubmissionClient.kt +++ b/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/api/ExtSubmissionClient.kt @@ -1,8 +1,8 @@ package ac.uk.ebi.biostd.client.api import ac.uk.ebi.biostd.client.dto.ExtPageQuery -import ac.uk.ebi.biostd.client.extensions.linkedMultiValueMapOf import ac.uk.ebi.biostd.client.integration.web.ExtSubmissionOperations +import ebi.ac.uk.commons.http.builder.linkedMultiValueMapOf import ebi.ac.uk.commons.http.ext.RequestParams import ebi.ac.uk.commons.http.ext.getForObject import ebi.ac.uk.commons.http.ext.post diff --git a/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/api/StatsClient.kt b/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/api/StatsClient.kt index 4256ae8ddc..4ec0c5575d 100644 --- a/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/api/StatsClient.kt +++ b/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/api/StatsClient.kt @@ -1,8 +1,8 @@ package ac.uk.ebi.biostd.client.api -import ac.uk.ebi.biostd.client.extensions.httpHeadersOf -import ac.uk.ebi.biostd.client.extensions.linkedMultiValueMapOf import ac.uk.ebi.biostd.client.integration.web.StatsOperations +import ebi.ac.uk.commons.http.builder.httpHeadersOf +import ebi.ac.uk.commons.http.builder.linkedMultiValueMapOf import ebi.ac.uk.commons.http.ext.RequestParams import ebi.ac.uk.commons.http.ext.getForObject import ebi.ac.uk.commons.http.ext.postForObject diff --git a/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/common/BioWebClientRequestBuilder.kt b/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/common/BioWebClientRequestBuilder.kt index dfb5887ba4..009ebf0e84 100644 --- a/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/common/BioWebClientRequestBuilder.kt +++ b/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/common/BioWebClientRequestBuilder.kt @@ -1,8 +1,8 @@ package ac.uk.ebi.biostd.client.common -import ac.uk.ebi.biostd.client.extensions.linkedMultiValueMapOf import ac.uk.ebi.biostd.client.integration.web.SubmissionFilesConfig import ebi.ac.uk.api.STORAGE_MODE +import ebi.ac.uk.commons.http.builder.linkedMultiValueMapOf import ebi.ac.uk.model.constants.FILES import ebi.ac.uk.model.constants.PREFERRED_SOURCES import ebi.ac.uk.model.constants.SUBMISSION diff --git a/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/extensions/SubExt.kt b/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/extensions/SubExt.kt index d18b8e5cc2..43de0b7b99 100644 --- a/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/extensions/SubExt.kt +++ b/commons/commons-bio/src/main/kotlin/ebi/ac/uk/model/extensions/SubExt.kt @@ -89,7 +89,7 @@ var Submission.title: String? } /** - * Obtain the submission title attribute if present. + * Obtain the submission DOI attribute if present. */ var Submission.doi: String? get() = find(SubFields.DOI) diff --git a/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/extensions/Common.kt b/commons/commons-http/src/main/kotlin/ebi/ac/uk/commons/http/builder/HttpParamBuilders.kt similarity index 94% rename from client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/extensions/Common.kt rename to commons/commons-http/src/main/kotlin/ebi/ac/uk/commons/http/builder/HttpParamBuilders.kt index 49da7dee9c..248071bc4f 100644 --- a/client/bio-webclient/src/main/kotlin/ac/uk/ebi/biostd/client/extensions/Common.kt +++ b/commons/commons-http/src/main/kotlin/ebi/ac/uk/commons/http/builder/HttpParamBuilders.kt @@ -1,4 +1,4 @@ -package ac.uk.ebi.biostd.client.extensions +package ebi.ac.uk.commons.http.builder import org.springframework.http.HttpHeaders import org.springframework.util.LinkedMultiValueMap diff --git a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/itest/ITestListener.kt b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/itest/ITestListener.kt index 8e88ec80d1..88f4730063 100644 --- a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/itest/ITestListener.kt +++ b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/itest/ITestListener.kt @@ -9,6 +9,7 @@ import ac.uk.ebi.biostd.itest.wiremock.TestWireMockTransformer import ac.uk.ebi.biostd.itest.wiremock.TestWireMockTransformer.Companion.newTransformer import com.adobe.testing.s3mock.testcontainers.S3MockContainer import com.github.tomakehurst.wiremock.WireMockServer +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder.okForJson import com.github.tomakehurst.wiremock.client.WireMock import com.github.tomakehurst.wiremock.client.WireMock.post import com.github.tomakehurst.wiremock.core.WireMockConfiguration @@ -17,7 +18,6 @@ import ebi.ac.uk.db.MONGO_VERSION import ebi.ac.uk.db.MYSQL_SCHEMA import ebi.ac.uk.db.MYSQL_VERSION import ebi.ac.uk.extended.model.StorageMode -import mu.KotlinLogging import org.junit.platform.launcher.TestExecutionListener import org.junit.platform.launcher.TestPlan import org.testcontainers.containers.MongoDBContainer @@ -27,8 +27,6 @@ import java.io.File import java.nio.file.Files import java.time.Duration.ofSeconds -private val logger = KotlinLogging.logger {} - class ITestListener : TestExecutionListener { override fun testPlanExecutionStarted(testPlan: TestPlan) { mongoSetup() @@ -62,19 +60,19 @@ class ITestListener : TestExecutionListener { private fun ftpSetup() { ftpServer.start() - System.setProperty("app.security.filesProperties.ftpUser", ftpUser) - System.setProperty("app.security.filesProperties.ftpPassword", ftpPassword) + System.setProperty("app.security.filesProperties.ftpUser", FTP_USER) + System.setProperty("app.security.filesProperties.ftpPassword", FTP_PASSWORD) System.setProperty("app.security.filesProperties.ftpUrl", ftpServer.getUrl()) System.setProperty("app.security.filesProperties.ftpPort", ftpServer.ftpPort.toString()) } private fun fireSetup() { s3Container.start() - System.setProperty("app.fire.s3.accessKey", awsAccessKey) - System.setProperty("app.fire.s3.secretKey", awsSecretKey) - System.setProperty("app.fire.s3.region", awsRegion) + System.setProperty("app.fire.s3.accessKey", AWS_ACCESS_KEY) + System.setProperty("app.fire.s3.secretKey", AWS_SECRET_KEY) + System.setProperty("app.fire.s3.region", AWS_REGION) System.setProperty("app.fire.s3.endpoint", s3Container.httpEndpoint) - System.setProperty("app.fire.s3.bucket", defaultBucket) + System.setProperty("app.fire.s3.bucket", DEFAULT_BUCKET) fireServer.stubFor( post(WireMock.urlMatching("/objects")) @@ -95,12 +93,13 @@ class ITestListener : TestExecutionListener { System.setProperty("app.requestFilesPath", requestFilesPath.absolutePath) System.setProperty("app.security.filesProperties.filesDirPath", dropboxPath.absolutePath) System.setProperty("app.security.filesProperties.magicDirPath", magicDirPath.absolutePath) - System.setProperty("app.persistence.concurrency", persistenceConcurrency) + System.setProperty("app.persistence.concurrency", PERSISTENCE_CONCURRENCY) System.setProperty("app.persistence.enableFire", "${System.getProperty("enableFire").toBoolean()}") } private fun doiSetup() { - System.setProperty("app.doi.endpoint", "https://test.crossref.org/servlet/deposit") + doiServer.start() + System.setProperty("app.doi.endpoint", "${doiServer.baseUrl()}/deposit") System.setProperty("app.doi.uiUrl", "https://www.ebi.ac.uk/biostudies/") System.setProperty("app.doi.user", "a-user") System.setProperty("app.doi.password", "a-password") @@ -108,20 +107,20 @@ class ITestListener : TestExecutionListener { companion object { private val testAppFolder = Files.createTempDirectory("test-app-folder").toFile() - private const val defaultBucket = "bio-fire-bucket" - private const val awsAccessKey = "anyKey" - private const val awsSecretKey = "anySecret" - private const val awsRegion = "anyRegion" - private const val failFactorEnv = "ITEST_FAIL_FACTOR" - private const val persistenceConcurrency = "10" + private const val DEFAULT_BUCKET = "bio-fire-bucket" + private const val AWS_ACCESS_KEY = "anyKey" + private const val AWS_SECRET_KEY = "anySecret" + private const val AWS_REGION = "anyRegion" + private const val FAIL_FACTOR_ENV = "ITEST_FAIL_FACTOR" + private const val PERSISTENCE_CONCURRENCY = "10" - private const val ftpUser = "ftpUser" - private const val ftpPassword = "ftpPassword" + private const val FTP_USER = "ftpUser" + private const val FTP_PASSWORD = "ftpPassword" - internal const val fixedDelayEnv = "ITEST_FIXED_DELAY" + internal const val FIXED_DELAY_ENV = "ITEST_FIXED_DELAY" internal val nfsSubmissionPath = testAppFolder.createDirectory("submission") internal val fireSubmissionPath = testAppFolder.createDirectory("submission-fire") - internal val firePath = testAppFolder.createDirectory("fire-db") + private val firePath = testAppFolder.createDirectory("fire-db") internal val fireTempFolder = testAppFolder.createDirectory("fire-temp") internal val nfsFtpPath = testAppFolder.createDirectory("ftpPath") @@ -133,7 +132,8 @@ class ITestListener : TestExecutionListener { internal val magicDirPath = testAppFolder.createDirectory("magic") internal val dropboxPath = testAppFolder.createDirectory("dropbox") - private val fireServer: WireMockServer by lazy { createFireApiMock(s3Container) } + private val fireServer: WireMockServer by lazy { createFireApiMock() } + private val doiServer: WireMockServer by lazy { createDoiApiMock() } private val ftpServer = createFtpServer() private val mongoContainer = createMongoContainer() @@ -156,29 +156,36 @@ class ITestListener : TestExecutionListener { .withStartupCheckStrategy(MinimumDurationRunningStartupCheckStrategy(ofSeconds(MINIMUM_RUNNING_TIME))) private fun createMockS3Container(): S3MockContainer = S3MockContainer("latest") - .withInitialBuckets(defaultBucket) + .withInitialBuckets(DEFAULT_BUCKET) private fun createFtpServer(): FtpServer { return FtpServer.createServer( FtpConfig( sslConfig = SslConfig(File(this::class.java.getResource("/mykeystore.jks").toURI()), "123456"), - userName = ftpUser, - password = ftpPassword + userName = FTP_USER, + password = FTP_PASSWORD ) ) } - private fun createFireApiMock(s3MockContainer: S3MockContainer): WireMockServer { - val factor = System.getenv(failFactorEnv)?.toInt() - val delay = System.getenv(fixedDelayEnv)?.toLong() ?: 0L + private fun createDoiApiMock(): WireMockServer { + val doiServer = WireMockServer(WireMockConfiguration().dynamicPort()) + doiServer.stubFor(post("/deposit").willReturn(okForJson("ok"))) + + return doiServer + } + + private fun createFireApiMock(): WireMockServer { + val factor = System.getenv(FAIL_FACTOR_ENV)?.toInt() + val delay = System.getenv(FIXED_DELAY_ENV)?.toLong() ?: 0L val transformer = newTransformer( subFolder = fireSubmissionPath.toPath(), ftpFolder = fireFtpPath.toPath(), dbFolder = firePath.toPath(), failFactor = factor, fixedDelay = delay, - httpEndpoint = s3MockContainer.httpEndpoint, - defaultBucket = defaultBucket + httpEndpoint = s3Container.httpEndpoint, + defaultBucket = DEFAULT_BUCKET ) return WireMockServer(WireMockConfiguration().dynamicPort().extensions(transformer)) } diff --git a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SubmissionFileSourceTest.kt b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SubmissionFileSourceTest.kt index bfe1b193d7..4fa1654da7 100644 --- a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SubmissionFileSourceTest.kt +++ b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SubmissionFileSourceTest.kt @@ -35,6 +35,7 @@ import ebi.ac.uk.io.sources.PreferredSource.SUBMISSION import ebi.ac.uk.io.sources.PreferredSource.USER_SPACE import ebi.ac.uk.model.extensions.title import ebi.ac.uk.util.collections.second +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeAll @@ -55,6 +56,7 @@ import java.nio.file.Paths @Import(FilePersistenceConfig::class) @ExtendWith(SpringExtension::class) +@OptIn(ExperimentalCoroutinesApi::class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Transactional class SubmissionFileSourceTest( diff --git a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SubmissionPerformanceTest.kt b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SubmissionPerformanceTest.kt index 52a203a895..4fb9e3b2df 100644 --- a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SubmissionPerformanceTest.kt +++ b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/test/submission/submit/SubmissionPerformanceTest.kt @@ -5,7 +5,7 @@ import ac.uk.ebi.biostd.client.integration.web.BioWebClient import ac.uk.ebi.biostd.common.config.FilePersistenceConfig import ac.uk.ebi.biostd.itest.common.SecurityTestService import ac.uk.ebi.biostd.itest.entities.SuperUser -import ac.uk.ebi.biostd.itest.itest.ITestListener.Companion.fixedDelayEnv +import ac.uk.ebi.biostd.itest.itest.ITestListener.Companion.FIXED_DELAY_ENV import ac.uk.ebi.biostd.itest.itest.ITestListener.Companion.tempFolder import ac.uk.ebi.biostd.itest.itest.getWebClient import ebi.ac.uk.dsl.tsv.line @@ -42,11 +42,11 @@ class SubmissionPerformanceTest( } @Test - @EnabledIfEnvironmentVariable(named = fixedDelayEnv, matches = "\\d+") + @EnabledIfEnvironmentVariable(named = FIXED_DELAY_ENV, matches = "\\d+") @EnabledIfSystemProperty(named = "enableFire", matches = "true") fun `With many files`() { val files = 100 - val delay = System.getenv(fixedDelayEnv).toLong() + val delay = System.getenv(FIXED_DELAY_ENV).toLong() val subFiles = (1..files).map { tempFolder.createFile("$it.txt") } webClient.uploadFiles(subFiles) diff --git a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/wiremock/TestWireMockTransformer.kt b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/wiremock/TestWireMockTransformer.kt index 8ad9a23768..51fe2e1bfd 100644 --- a/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/wiremock/TestWireMockTransformer.kt +++ b/submission/submission-webapp/src/itest/kotlin/ac/uk/ebi/biostd/itest/wiremock/TestWireMockTransformer.kt @@ -25,7 +25,7 @@ import org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR import java.nio.file.Path import kotlin.random.Random -class TestWireMockTransformer constructor( +class TestWireMockTransformer( private val failFactor: Int?, private val fixedDelay: Long, private val handlers: List, diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt index 35c0c7e7c0..00704f3732 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/exceptions/DoiExceptions.kt @@ -4,9 +4,7 @@ class MissingDoiFieldException(field: String) : RuntimeException("The required D class MissingTitleException : RuntimeException("A title is required for DOI registration") -class InvalidOrgNamesException( - organizations: List -) : RuntimeException("The following organization names are empty: ${organizations.joinToString(", ")}") +class InvalidOrgNameException(org: String) : RuntimeException("The following organization name is empty: '$org'") class InvalidOrgException : RuntimeException("Organizations are required to have an accession") @@ -19,4 +17,9 @@ class InvalidAuthorAffiliationException( organization: String, ) : RuntimeException("The organization '$organization' affiliated to the author '$author' could not be found") -class InvalidDoiException : RuntimeException("The given DOI should match the previous version") +class RemovedDoiException(previous: String) : RuntimeException("The previous DOI: '$previous' cannot be removed") + +class InvalidDoiException( + given: String, + previous: String, +) : RuntimeException("The given DOI '$given' should match the previous DOI '$previous'") diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt index 6e46f4d137..dd97a715d9 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/model/DoiRequest.kt @@ -3,7 +3,6 @@ package ac.uk.ebi.biostd.submission.model import ebi.ac.uk.util.collections.ifNotEmpty import org.redundent.kotlin.xml.PrintOptions import org.redundent.kotlin.xml.xml -import java.time.Instant data class Contributor( val name: String, @@ -15,6 +14,7 @@ data class Contributor( class DoiRequest( private val accNo: String, private val title: String, + private val timestamp: String, private val instanceUrl: String, private val contributors: List, ) { @@ -22,7 +22,6 @@ class DoiRequest( get() = "$BS_DOI_ID/$accNo" fun asXmlRequest(): String { - val timestamp = Instant.now().epochSecond.toString() return xml("doi_batch") { xmlns = XML_NAMESPACE attribute("xmlns:xsi", XML_SCHEMA_INSTANCE) diff --git a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt index 3d20c18033..a4dc792dca 100644 --- a/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt +++ b/submission/submitter/src/main/kotlin/ac/uk/ebi/biostd/submission/service/DoiService.kt @@ -5,13 +5,16 @@ import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorAffiliationException import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorNameException import ac.uk.ebi.biostd.submission.exceptions.InvalidDoiException import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgException -import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgNamesException +import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgNameException import ac.uk.ebi.biostd.submission.exceptions.MissingAuthorAffiliationException import ac.uk.ebi.biostd.submission.exceptions.MissingDoiFieldException import ac.uk.ebi.biostd.submission.exceptions.MissingTitleException +import ac.uk.ebi.biostd.submission.exceptions.RemovedDoiException import ac.uk.ebi.biostd.submission.model.Contributor import ac.uk.ebi.biostd.submission.model.DoiRequest import ac.uk.ebi.biostd.submission.model.SubmitRequest +import ebi.ac.uk.commons.http.builder.httpHeadersOf +import ebi.ac.uk.commons.http.builder.linkedMultiValueMapOf import ebi.ac.uk.commons.http.ext.RequestParams import ebi.ac.uk.commons.http.ext.post import ebi.ac.uk.io.FileUtils @@ -20,14 +23,13 @@ import ebi.ac.uk.model.Submission import ebi.ac.uk.model.constants.SubFields.DOI import ebi.ac.uk.model.extensions.allSections import ebi.ac.uk.model.extensions.title -import ebi.ac.uk.util.collections.ifNotEmpty import mu.KotlinLogging import org.springframework.core.io.FileSystemResource -import org.springframework.http.HttpHeaders +import org.springframework.http.HttpHeaders.CONTENT_TYPE import org.springframework.http.MediaType.MULTIPART_FORM_DATA -import org.springframework.util.LinkedMultiValueMap import org.springframework.web.reactive.function.client.WebClient import java.nio.file.Files +import java.time.Instant private val logger = KotlinLogging.logger {} @@ -41,7 +43,9 @@ class DoiService( val previousDoi = rqt.previousVersion?.doi if (previousDoi != null) { - require(doi.value == previousDoi) { throw InvalidDoiException() } + val value = doi.value + requireNotNull(value) { throw RemovedDoiException(previousDoi) } + require(value == previousDoi) { throw InvalidDoiException(value, previousDoi) } return previousDoi } @@ -50,18 +54,19 @@ class DoiService( private fun registerDoi(accNo: String, rqt: SubmitRequest): String { val sub = rqt.submission + val timestamp = Instant.now().epochSecond.toString() val title = requireNotNull(sub.title) { throw MissingTitleException() } - val request = DoiRequest(accNo, title, properties.uiUrl, getContributors(sub)) + val request = DoiRequest(accNo, title, timestamp, properties.uiUrl, getContributors(sub)) val requestFile = Files.createTempFile("${TEMP_FILE_NAME}_$accNo", ".xml").toFile() FileUtils.writeContent(requestFile, request.asXmlRequest()) - val headers = HttpHeaders().apply { contentType = MULTIPART_FORM_DATA } - val body = LinkedMultiValueMap().apply { - add(USER_PARAM, properties.user) - add(PASSWORD_PARAM, properties.password) - add(OPERATION_PARAM, OPERATION_PARAM_VALUE) - add(FILE_PARAM, FileSystemResource(requestFile)) - } + val headers = httpHeadersOf(CONTENT_TYPE to MULTIPART_FORM_DATA) + val body = linkedMultiValueMapOf( + USER_PARAM to properties.user, + PASSWORD_PARAM to properties.password, + OPERATION_PARAM to OPERATION_PARAM_VALUE, + FILE_PARAM to FileSystemResource(requestFile), + ) webClient.post(properties.endpoint, RequestParams(headers, body)) logger.info { "$accNo ${rqt.owner} Registered DOI: '${request.doi}'" } @@ -71,49 +76,62 @@ class DoiService( private fun getContributors(sub: Submission): List { val organizations = getOrganizations(sub) - return sub.allSections() - .filter { it.type.lowercase() == AUTHOR_TYPE } + val contributors = sub.allSections().filter { it.type == AUTHOR_TYPE } + validateContributors(contributors, organizations) + + return contributors.map { it.asContributor(organizations) } + } + + private fun validateContributors(contributors: List
, organizations: Map) { + fun validate(contributor: Section) { + val names = requireNotNull(contributor.find(NAME_ATTR)) { throw InvalidAuthorNameException() } + val org = requireNotNull(contributor.find(AFFILIATION_ATTR)) { throw MissingAuthorAffiliationException() } + requireNotNull(organizations[org]) { throw InvalidAuthorAffiliationException(names, org) } + } + + contributors .ifEmpty { throw MissingDoiFieldException(AUTHOR_TYPE) } - .map { it.asContributor(organizations) } + .forEach(::validate) } - private fun Section.asContributor(orgs: Map): Contributor { - val attrsMap = attributes.associateBy({ it.name.lowercase() }, { it.value }) - val names = requireNotNull(attrsMap[NAME_ATTR]) { throw InvalidAuthorNameException() } - val affiliation = requireNotNull(attrsMap[AFFILIATION_ATTR]) { throw MissingAuthorAffiliationException() } - val org = requireNotNull(orgs[affiliation]) { throw InvalidAuthorAffiliationException(names, affiliation) } + private fun Section.asContributor(organizations: Map): Contributor { + val names = find(NAME_ATTR)!! + val affiliation = find(AFFILIATION_ATTR)!! return Contributor( name = names.substringBeforeLast(" ", ""), surname = names.substringAfterLast(" "), - affiliation = org, - orcid = attrsMap[ORCID_ATTR] + affiliation = organizations[affiliation]!!, + orcid = find(ORCID_ATTR) ) } private fun getOrganizations(sub: Submission): Map { - val organizations = sub.allSections() - .filter { it.type.lowercase() == ORG_TYPE } - .ifEmpty { throw MissingDoiFieldException(ORG_TYPE) } - .associateBy( - { requireNotNull(it.accNo) { throw InvalidOrgException() } }, - { org -> org.attributes.find { it.name.lowercase() == NAME_ATTR }?.value.orEmpty() }, - ) + val organizations = sub.allSections().filter { it.type == ORG_TYPE } + validateOrganizations(organizations) + + return organizations.associateBy({ it.accNo!! }, { it.find(NAME_ATTR)!! }) + } - organizations.entries - .filter { it.value.isEmpty() } - .ifNotEmpty { entries -> throw InvalidOrgNamesException(entries.map { it.key }) } + private fun validateOrganizations(organizations: List
) { + fun validate(org: Section) { + val accNo = org.accNo + requireNotNull(accNo) { throw InvalidOrgException() } + requireNotNull(org.find(NAME_ATTR)) { throw InvalidOrgNameException(accNo) } + } - return organizations + organizations + .ifEmpty { throw MissingDoiFieldException(ORG_TYPE) } + .forEach(::validate) } companion object { - internal const val AFFILIATION_ATTR = "affiliation" - internal const val NAME_ATTR = "name" - internal const val ORCID_ATTR = "orcid" + internal const val AFFILIATION_ATTR = "Affiliation" + internal const val NAME_ATTR = "Name" + internal const val ORCID_ATTR = "ORCID" - internal const val ORG_TYPE = "organization" - internal const val AUTHOR_TYPE = "author" + internal const val ORG_TYPE = "Organization" + internal const val AUTHOR_TYPE = "Author" internal const val FILE_PARAM = "fname" internal const val OPERATION_PARAM = "operation" diff --git a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt index dae304f31d..a2b56f98a7 100644 --- a/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt +++ b/submission/submitter/src/test/kotlin/ac/uk/ebi/biostd/submission/service/DoiServiceTest.kt @@ -5,10 +5,11 @@ import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorAffiliationException import ac.uk.ebi.biostd.submission.exceptions.InvalidAuthorNameException import ac.uk.ebi.biostd.submission.exceptions.InvalidDoiException import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgException -import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgNamesException +import ac.uk.ebi.biostd.submission.exceptions.InvalidOrgNameException import ac.uk.ebi.biostd.submission.exceptions.MissingAuthorAffiliationException import ac.uk.ebi.biostd.submission.exceptions.MissingDoiFieldException import ac.uk.ebi.biostd.submission.exceptions.MissingTitleException +import ac.uk.ebi.biostd.submission.exceptions.RemovedDoiException import ac.uk.ebi.biostd.submission.model.DoiRequest.Companion.BS_DOI_ID import ac.uk.ebi.biostd.submission.model.SubmitRequest import ac.uk.ebi.biostd.submission.service.DoiService.Companion.FILE_PARAM @@ -41,8 +42,6 @@ import org.springframework.http.HttpHeaders.CONTENT_TYPE import org.springframework.util.LinkedMultiValueMap import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec -import java.nio.file.Files -import java.nio.file.Paths import java.time.Instant import java.time.OffsetDateTime import java.time.ZoneOffset.UTC @@ -101,10 +100,9 @@ class DoiServiceTest( val body = bodySlot.captured val headers = headersSlot.captured val requestFile = body[FILE_PARAM]!!.first() as FileSystemResource - val expectedXml = Files.readString(Paths.get("src/test/resources/ExpectedDOIRequest.xml")) assertThat(doi).isEqualTo("$BS_DOI_ID/$TEST_ACC_NO") - assertThat(requestFile.file.readText()).isEqualToIgnoringWhitespace(expectedXml) + assertThat(requestFile.file.readText()).isEqualToIgnoringWhitespace(EXPECTED_DOI_REQUEST) assertThat(body[USER_PARAM]!!.first()).isEqualTo(properties.user) assertThat(body[PASSWORD_PARAM]!!.first()).isEqualTo(properties.password) assertThat(body[OPERATION_PARAM]!!.first()).isEqualTo(OPERATION_PARAM_VALUE) @@ -156,9 +154,11 @@ class DoiServiceTest( @Test fun `invalid given DOI`() { + val doi = "10.287.71/$TEST_ACC_NO" + val previousDoi = "$BS_DOI_ID/$TEST_ACC_NO" val submission = submission { title = "Test Submission" - attribute("DOI", "10.287.71/$TEST_ACC_NO") + attribute("DOI", doi) section("Study") { attribute("Type", "Experiment") @@ -166,12 +166,34 @@ class DoiServiceTest( } every { submitRequest.submission } returns submission - every { previousVersion.doi } returns "$BS_DOI_ID/$TEST_ACC_NO" + every { previousVersion.doi } returns previousDoi every { submitRequest.previousVersion } returns previousVersion val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } - assertThat(exception.message).isEqualTo("The given DOI should match the previous version") + assertThat(exception.message).isEqualTo("The given DOI '$doi' should match the previous DOI '$previousDoi'") + verify(exactly = 0) { webClient.post() } + } + + @Test + fun `removed DOI`() { + val previousDoi = "$BS_DOI_ID/$TEST_ACC_NO" + val submission = submission { + title = "Test Submission" + attribute("DOI", null) + + section("Study") { + attribute("Type", "Experiment") + } + } + + every { submitRequest.submission } returns submission + every { previousVersion.doi } returns previousDoi + every { submitRequest.previousVersion } returns previousVersion + + val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } + + assertThat(exception.message).isEqualTo("The previous DOI: '$previousDoi' cannot be removed") verify(exactly = 0) { webClient.post() } } @@ -207,7 +229,7 @@ class DoiServiceTest( val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } verify(exactly = 0) { webClient.post() } - assertThat(exception.message).isEqualTo("The required DOI field 'organization' could not be found") + assertThat(exception.message).isEqualTo("The required DOI field 'Organization' could not be found") } @Test @@ -240,7 +262,7 @@ class DoiServiceTest( section("Study") { section("Organization") { accNo = "o1" - attribute("Institue", "American Society") + attribute("Name", "American Society") } section("Organization") { @@ -257,10 +279,10 @@ class DoiServiceTest( every { submitRequest.submission } returns submission - val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } + val exception = assertThrows { testInstance.calculateDoi(TEST_ACC_NO, submitRequest) } verify(exactly = 0) { webClient.post() } - assertThat(exception.message).isEqualTo("The following organization names are empty: o1, o3") + assertThat(exception.message).isEqualTo("The following organization name is empty: 'o3'") } @Test @@ -326,7 +348,7 @@ class DoiServiceTest( fun `invalid affiliation`() { val submission = submission { title = "Test Submission" - attribute("DOI", "") + attribute("DOI", null) section("Study") { section("Organization") { @@ -362,5 +384,49 @@ class DoiServiceTest( user = "a-user", password = "a-password", ) + + private const val EXPECTED_DOI_REQUEST = """ + + + 1600683060 + 1600683060 + + EMBL-EBI + biostudies@ebi.ac.uk + + EMBL-EBI + + + + + + BioStudies Database + + + + + + John + Doe + EMBL + 12-32-45-82 + + + + Test Submission + + + 10.6019/S-TEST123 + https://www.biostudies.ac.uk/studies/S-TEST123 + + + + + + """ } } diff --git a/submission/submitter/src/test/resources/ExpectedDOIRequest.xml b/submission/submitter/src/test/resources/ExpectedDOIRequest.xml deleted file mode 100644 index e3b02a9a69..0000000000 --- a/submission/submitter/src/test/resources/ExpectedDOIRequest.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - 1600683060 - 1600683060 - - EMBL-EBI - biostudies@ebi.ac.uk - - EMBL-EBI - - - - - - BioStudies Database - - - - - - John - Doe - EMBL - 12-32-45-82 - - - - Test Submission - - - 10.6019/S-TEST123 - https://www.biostudies.ac.uk/studies/S-TEST123 - - - - -