From accb23e10dda72d1d6e04d6b24f6588f1b59e37e Mon Sep 17 00:00:00 2001 From: Dean Hiller Date: Fri, 23 Dec 2016 09:26:54 -0700 Subject: [PATCH 01/10] add error information for when sonatype fails which seems to be happening a lot to me --- .../infra/SimplifiedHttpJsonRestClient.groovy | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy b/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy index 4e9d5bf..da33569 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy @@ -1,5 +1,7 @@ package io.codearte.gradle.nexus.infra +import org.apache.http.client.HttpResponseException; + import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import groovyx.net.http.ContentType @@ -51,8 +53,17 @@ class SimplifiedHttpJsonRestClient { Map params = createAndInitializeCallParametersMap() params.body = content log.debug("POST request content: $content") - //TODO: Add better error handling (e.g. display error message received from server, not only 500 + not fail on 404 in 'text/html') - HttpResponseDecorator response = (HttpResponseDecorator)restClient.post(params) - log.warn("POST response data: ${response.data}") + try { + //TODO: Add better error handling (e.g. display error message received from server, not only 500 + not fail on 404 in 'text/html') + HttpResponseDecorator response = (HttpResponseDecorator)restClient.post(params) + log.warn("POST response data: ${response.data}") + } catch(groovyx.net.http.HttpResponseException e) { + //Apache' HttpResponseException ONLY puts the 2nd param in the e.getMessage which will be printed so + //put all information there (status code, error str, body of response in case they put more error information there) + + HttpResponseDecorator resp = e.getResponse(); + String message = "${resp.statusLine.statusCode}:${resp.statusLine.reasonPhrase} body=${resp.data}" + throw new HttpResponseException(e.getStatusCode(), message) + } } } From ecdc29634d3dfd4feb00f2e6f0fd63caad391603 Mon Sep 17 00:00:00 2001 From: Dean Hiller Date: Fri, 23 Dec 2016 09:30:31 -0700 Subject: [PATCH 02/10] add log --- .../gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy b/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy index da33569..9e557e6 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy @@ -63,6 +63,7 @@ class SimplifiedHttpJsonRestClient { HttpResponseDecorator resp = e.getResponse(); String message = "${resp.statusLine.statusCode}:${resp.statusLine.reasonPhrase} body=${resp.data}" + log.error("POST response failed. ${message}") throw new HttpResponseException(e.getStatusCode(), message) } } From 765ee29ae861213788bb8e51784e0652cd4ff53f Mon Sep 17 00:00:00 2001 From: Roman Rodov Date: Fri, 17 Mar 2017 10:13:03 +1100 Subject: [PATCH 03/10] - Fixed the case where there are many "closed" repos and only one open one (note to promote in this case you MUST close and promote together) - Added feature toggle for "nexus-staging.autoDropAfterRelease" via project property as per #37 - Fixed typos and tests --- .../gradle/nexus/CloseRepositoryTask.groovy | 10 ++- .../gradle/nexus/PromoteRepositoryTask.groovy | 11 +-- .../nexus/logic/OperationRetrier.groovy | 2 +- .../nexus/logic/RepositoryFetcher.groovy | 18 ++--- .../nexus/logic/RepositoryPromoter.groovy | 12 +++- .../functional/BasicFunctionalSpec.groovy | 2 +- .../functional/MockedFunctionalSpec.groovy | 71 ++++++++++++++----- .../nexus/logic/RepositoryFetcherSpec.groovy | 11 +-- 8 files changed, 94 insertions(+), 43 deletions(-) diff --git a/src/main/groovy/io/codearte/gradle/nexus/CloseRepositoryTask.groovy b/src/main/groovy/io/codearte/gradle/nexus/CloseRepositoryTask.groovy index 2372ecf..c770e22 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/CloseRepositoryTask.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/CloseRepositoryTask.groovy @@ -5,10 +5,17 @@ import io.codearte.gradle.nexus.logic.OperationRetrier import io.codearte.gradle.nexus.logic.RepositoryCloser import io.codearte.gradle.nexus.logic.RepositoryFetcher import io.codearte.gradle.nexus.logic.StagingProfileFetcher +import org.gradle.api.internal.tasks.options.Option +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction @CompileStatic -public class CloseRepositoryTask extends BaseStagingTask { +class CloseRepositoryTask extends BaseStagingTask { + + @Input + @Optional + String stagingRepositoryId @TaskAction void doAction() { @@ -19,6 +26,7 @@ public class CloseRepositoryTask extends BaseStagingTask { String stagingProfileId = fetchAndCacheStagingProfileId(stagingProfileFetcher) String repositoryId = retrier.doWithRetry { repositoryFetcher.getOpenRepositoryIdForStagingProfileId(stagingProfileId) } + stagingRepositoryId = repositoryId repositoryCloser.closeRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId) } } diff --git a/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy b/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy index ea0a268..a5a1012 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy @@ -8,7 +8,7 @@ import io.codearte.gradle.nexus.logic.StagingProfileFetcher import org.gradle.api.tasks.TaskAction @CompileStatic -public class PromoteRepositoryTask extends BaseStagingTask { +class PromoteRepositoryTask extends BaseStagingTask { @TaskAction void doAction() { @@ -19,15 +19,18 @@ public class PromoteRepositoryTask extends BaseStagingTask { tryToTakeStagingProfileIdFromCloseRepositoryTask() String stagingProfileId = fetchAndCacheStagingProfileId(stagingProfileFetcher) - String repositoryId = retrier.doWithRetry { repositoryFetcher.getClosedRepositoryIdForStagingProfileId(stagingProfileId) } - repositoryPromoter.promoteRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId) + String repositoryId = project.tasks.withType(CloseRepositoryTask)[0].stagingRepositoryId ?: + retrier.doWithRetry { repositoryFetcher.getClosedRepositoryIdForStagingProfileId(stagingProfileId) } + def autoDropAfterRelease = Boolean.valueOf(project.properties["nexus-staging.autoDropAfterRelease"] as String) + repositoryPromoter.promoteRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId, autoDropAfterRelease) } private void tryToTakeStagingProfileIdFromCloseRepositoryTask() { if (getStagingProfileId() != null) { return } - String stagingProfileIdFromCloseRepositoryTask = project.tasks.withType(CloseRepositoryTask)[0].getStagingProfileId() + String stagingProfileIdFromCloseRepositoryTask = project.tasks.withType(CloseRepositoryTask)[0].stagingProfileId + if (stagingProfileIdFromCloseRepositoryTask != null) { logger.debug("Reusing staging profile id from closeRepository task: $stagingProfileIdFromCloseRepositoryTask") setStagingProfileId(stagingProfileIdFromCloseRepositoryTask) diff --git a/src/main/groovy/io/codearte/gradle/nexus/logic/OperationRetrier.groovy b/src/main/groovy/io/codearte/gradle/nexus/logic/OperationRetrier.groovy index 3f7a165..7205024 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/logic/OperationRetrier.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/logic/OperationRetrier.groovy @@ -19,7 +19,7 @@ class OperationRetrier { this.delayBetweenRetries = delayBetweenRetries } - public T doWithRetry(Closure operation) { + T doWithRetry(Closure operation) { int counter = 0 int numberOfAttempts = numberOfRetries + 1 while (true) { diff --git a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcher.groovy b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcher.groovy index 1e7701d..4919cfa 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcher.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcher.groovy @@ -24,24 +24,16 @@ class RepositoryFetcher extends BaseOperationExecutor { private String parseResponseAndGetRepositoryIdInGivenState(Map responseAsMap, String repositoryState) { def repository = verifyThatOneRepositoryAndReturnIt(responseAsMap, repositoryState) - verifyReceivedRepositoryState(repository, repositoryState) log.debug("Received 1 '$repositoryState' repository with id: ${repository.repositoryId}") return repository.repositoryId } private Map verifyThatOneRepositoryAndReturnIt(Map responseAsMap, String repositoryState) { - int numberOfRespositories = responseAsMap.data.size() - if (numberOfRespositories != 1) { - throw new WrongNumberOfRepositories(numberOfRespositories, repositoryState) - } - Map repository = responseAsMap.data[0] as Map - return repository - } - - private void verifyReceivedRepositoryState(Map repository, String expectedRepositoryState) { - if (repository.type != expectedRepositoryState) { - throw new IllegalArgumentException( - "Unexpected state of received repository. Received ${repository.type}, expected $expectedRepositoryState") + def repositoryInGivenState = { it.type == repositoryState } + int numberOfRepositories = responseAsMap.data.count(repositoryInGivenState) + if (numberOfRepositories != 1) { + throw new WrongNumberOfRepositories(numberOfRepositories, repositoryState) } + return responseAsMap.data.find(repositoryInGivenState) as Map } } diff --git a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy index 788e985..b2eb26d 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy @@ -9,10 +9,18 @@ import groovy.util.logging.Slf4j @Slf4j class RepositoryPromoter extends AbstractStagingOperationExecutor { - void promoteRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) { + void promoteRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId, boolean autoDropAfterRelease) { log.info("Promoting repository '$repositoryId' with staging profile '$stagingProfileId'") Map postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId) + if (autoDropAfterRelease) { + Map data = postContent.data as Map + data.autoDropAfterRelease = true + } client.post(nexusUrl + "/staging/profiles/$stagingProfileId/promote", postContent) - log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been promotted") + log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been promoted") + } + + void promoteRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) { + promoteRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId, false) } } diff --git a/src/test/groovy/io/codearte/gradle/nexus/functional/BasicFunctionalSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/functional/BasicFunctionalSpec.groovy index 8ad1fe2..02f71f3 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/functional/BasicFunctionalSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/functional/BasicFunctionalSpec.groovy @@ -54,6 +54,6 @@ class BasicFunctionalSpec extends BaseNexusStagingFunctionalSpec { then: result.wasExecuted(':promoteRepository') and: - result.standardOutput.contains("has been promotted") //TODO: Match with regexp + result.standardOutput.contains("has been promoted") //TODO: Match with regexp } } \ No newline at end of file diff --git a/src/test/groovy/io/codearte/gradle/nexus/functional/MockedFunctionalSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/functional/MockedFunctionalSpec.groovy index a1b3126..01ec4e8 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/functional/MockedFunctionalSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/functional/MockedFunctionalSpec.groovy @@ -7,12 +7,20 @@ import io.codearte.gradle.nexus.logic.FetcherResponseTrait import org.gradle.api.logging.LogLevel import org.junit.Rule -import static com.github.tomakehurst.wiremock.client.WireMock.* +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse +import static com.github.tomakehurst.wiremock.client.WireMock.containing +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo +import static com.github.tomakehurst.wiremock.client.WireMock.get +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor +import static com.github.tomakehurst.wiremock.client.WireMock.post +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo +import static com.github.tomakehurst.wiremock.client.WireMock.verify class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements FetcherResponseTrait { @Rule - public WireMockRule wireMockRule = new WireMockRule(8089); + public WireMockRule wireMockRule = new WireMockRule(8089) protected static final String stagingProfileId = "93c08fdebde1ff" @@ -75,11 +83,11 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet "promoteRepository" | "closed" } - def "should reuse stagingProfileId from closeRepository in promoteRepository when called together"() { + def "should reuse stagingProfileId AND stagingRepositoryId from closeRepository in promoteRepository when called together"() { given: stubGetStagingProfilesWithJson(this.getClass().getResource("/io/codearte/gradle/nexus/logic/2stagingProfilesShrunkResponse.json").text) and: - stubGetOneOpenRepositoryInFirstCallAndOneClosedIntheNext(stagingProfileId) + stubGetOneOpenRepositoryAndOneClosedInFirstCallAndTwoClosedInTheNext(stagingProfileId) and: stubSuccessfulCloseRepositoryWithProfileId(stagingProfileId) and: @@ -95,7 +103,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet result.wasExecuted("closeRepository") result.wasExecuted("promoteRepository") and: - verify(2, getRequestedFor(urlEqualTo("/staging/profile_repositories/$stagingProfileId"))) + verify(1, getRequestedFor(urlEqualTo("/staging/profile_repositories/$stagingProfileId"))) verify(1, getRequestedFor(urlEqualTo("/staging/profiles"))) } @@ -116,7 +124,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet def "should retry promotion when repository has not been already closed"() { given: - stubGetOneOpenRepositoryInFirstCallAndOneClosedIntheNext(stagingProfileId) + stubGetOneOpenRepositoryInFirstCallAndOneClosedInTheNext(stagingProfileId) and: stubSuccessfulCloseRepositoryWithProfileId(stagingProfileId) and: @@ -157,7 +165,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet def "should call close and promote in closeAndPromoteRepository task"() { given: - stubGetOneOpenRepositoryInFirstCallAndOneClosedIntheNext(stagingProfileId) + stubGetOneOpenRepositoryAndOneClosedInFirstCallAndTwoClosedInTheNext(stagingProfileId) and: stubSuccessfulCloseRepositoryWithProfileId(stagingProfileId) and: @@ -228,7 +236,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") - .withBody(responseAsJson))); + .withBody(responseAsJson))) } private void stubGetOneRepositoryWithProfileIdAndContent(String stagingProfileId, Map response) { @@ -238,7 +246,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") - .withBody(JsonOutput.toJson(response)))); + .withBody(JsonOutput.toJson(response)))) } private void stubSuccessfulCloseRepositoryWithProfileId(String stagingProfileId) { @@ -247,7 +255,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet .withHeader("Accept", containing("application/json")) .willReturn(aResponse() .withStatus(200) - .withHeader("Content-Type", "application/json"))); + .withHeader("Content-Type", "application/json"))) } private void stubSuccessfulPromoteRepositoryWithProfileId(String stagingProfileId) { @@ -256,10 +264,10 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet .withHeader("Accept", containing("application/json")) .willReturn(aResponse() .withStatus(200) - .withHeader("Content-Type", "application/json"))); + .withHeader("Content-Type", "application/json"))) } - private void stubGetOneOpenRepositoryInFirstCallAndOneClosedIntheNext(String stagingProfileId) { + private void stubGetOneOpenRepositoryAndOneClosedInFirstCallAndTwoClosedInTheNext(String stagingProfileId) { stubFor(get(urlEqualTo("/staging/profile_repositories/$stagingProfileId")).inScenario("State") .whenScenarioStateIs(Scenario.STARTED) .withHeader("Content-Type", containing("application/json")) @@ -267,9 +275,11 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") - .withBody(JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("open", "ignored")]))) + .withBody( + JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("open", "ignored"), aRepoInStateAndId("closed", "ignoredClosed")])) + ) ) - .willSetStateTo("CLOSED")); + .willSetStateTo("CLOSED")) stubFor(get(urlEqualTo("/staging/profile_repositories/$stagingProfileId")).inScenario("State") .whenScenarioStateIs("CLOSED") @@ -278,7 +288,36 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") - .withBody(JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("closed", "ignored")]))) - )); + .withBody( + JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("closed", "ignored"), aRepoInStateAndId("closed", "ignoredClosed")])) + ) + ) + ) + } + + private void stubGetOneOpenRepositoryInFirstCallAndOneClosedInTheNext(String stagingProfileId) { + stubFor(get(urlEqualTo("/staging/profile_repositories/$stagingProfileId")).inScenario("State") + .whenScenarioStateIs(Scenario.STARTED) + .withHeader("Content-Type", containing("application/json")) + .withHeader("Accept", containing("application/json")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("open", "ignored")])) + ) + ) + .willSetStateTo("CLOSED")) + + stubFor(get(urlEqualTo("/staging/profile_repositories/$stagingProfileId")).inScenario("State") + .whenScenarioStateIs("CLOSED") + .withHeader("Content-Type", containing("application/json")) + .withHeader("Accept", containing("application/json")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("closed", "ignored")]))) + ) + ) } } diff --git a/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcherSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcherSpec.groovy index 12a75c0..37104e2 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcherSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcherSpec.groovy @@ -86,12 +86,13 @@ class RepositoryFetcherSpec extends BaseOperationExecutorSpec implements Fetcher when: fetcher."get${expectedState.capitalize()}RepositoryIdForStagingProfileId"(TEST_STAGING_PROFILE_ID) then: - def e = thrown(IllegalArgumentException) - e.message == "Unexpected state of received repository. Received $receivedState, expected $expectedState".toString() + def e = thrown(WrongNumberOfRepositories) + e.message == "Wrong number of received repositories in state '$expectedState'. Expected 1, received 0" where: - expectedState || receivedState - "open" || "closed" - "closed" || "open" + + expectedState | receivedState + "open" | "closed" + "closed" | "open" } private Map anOpenRepo() { From 70552fed8d1a3d6bda80c8e0dbc3fff10ec1e838 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Sat, 18 Mar 2017 13:03:34 +0100 Subject: [PATCH 04/10] Minor tweaks PR changes --- .../gradle/nexus/PromoteRepositoryTask.groovy | 21 ++++++++++++++----- .../nexus/logic/RepositoryFetcher.groovy | 4 ++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy b/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy index a5a1012..98e9d07 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy @@ -15,13 +15,12 @@ class PromoteRepositoryTask extends BaseStagingTask { StagingProfileFetcher stagingProfileFetcher = createFetcherWithGivenClient(createClient()) RepositoryFetcher repositoryFetcher = createRepositoryFetcherWithGivenClient(createClient()) RepositoryPromoter repositoryPromoter = createRepositoryPromoterWithGivenClient(createClient()) - OperationRetrier retrier = createOperationRetrier() tryToTakeStagingProfileIdFromCloseRepositoryTask() String stagingProfileId = fetchAndCacheStagingProfileId(stagingProfileFetcher) - String repositoryId = project.tasks.withType(CloseRepositoryTask)[0].stagingRepositoryId ?: - retrier.doWithRetry { repositoryFetcher.getClosedRepositoryIdForStagingProfileId(stagingProfileId) } - def autoDropAfterRelease = Boolean.valueOf(project.properties["nexus-staging.autoDropAfterRelease"] as String) + + String repositoryId = getRepositoryIdFromCloseTaskOrFromServer(stagingProfileId, repositoryFetcher) + def autoDropAfterRelease = Boolean.valueOf(project.properties["nexus-staging.autoDropAfterRelease"] as String) //TODO: Remove repositoryPromoter.promoteRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId, autoDropAfterRelease) } @@ -29,11 +28,23 @@ class PromoteRepositoryTask extends BaseStagingTask { if (getStagingProfileId() != null) { return } - String stagingProfileIdFromCloseRepositoryTask = project.tasks.withType(CloseRepositoryTask)[0].stagingProfileId + String stagingProfileIdFromCloseRepositoryTask = getCloseRepositoryTask().stagingProfileId if (stagingProfileIdFromCloseRepositoryTask != null) { logger.debug("Reusing staging profile id from closeRepository task: $stagingProfileIdFromCloseRepositoryTask") setStagingProfileId(stagingProfileIdFromCloseRepositoryTask) } } + + private CloseRepositoryTask getCloseRepositoryTask() { + return project.tasks.withType(CloseRepositoryTask)[0] + } + + private String getRepositoryIdFromCloseTaskOrFromServer(String stagingProfileId, RepositoryFetcher repositoryFetcher) { + //TODO: Add debug statement + OperationRetrier retrier = createOperationRetrier() + String repositoryId = getCloseRepositoryTask().stagingRepositoryId ?: + retrier.doWithRetry { repositoryFetcher.getClosedRepositoryIdForStagingProfileId(stagingProfileId) } + return repositoryId + } } diff --git a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcher.groovy b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcher.groovy index 4919cfa..1090796 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcher.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcher.groovy @@ -23,13 +23,13 @@ class RepositoryFetcher extends BaseOperationExecutor { } private String parseResponseAndGetRepositoryIdInGivenState(Map responseAsMap, String repositoryState) { - def repository = verifyThatOneRepositoryAndReturnIt(responseAsMap, repositoryState) + Map repository = verifyThatOneRepositoryAndReturnIt(responseAsMap, repositoryState) log.debug("Received 1 '$repositoryState' repository with id: ${repository.repositoryId}") return repository.repositoryId } private Map verifyThatOneRepositoryAndReturnIt(Map responseAsMap, String repositoryState) { - def repositoryInGivenState = { it.type == repositoryState } + Closure repositoryInGivenState = { it.type == repositoryState } int numberOfRepositories = responseAsMap.data.count(repositoryInGivenState) if (numberOfRepositories != 1) { throw new WrongNumberOfRepositories(numberOfRepositories, repositoryState) From 74b121e1e5d39b5b27a0ce375fc88b3b3afa5050 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Sat, 18 Mar 2017 13:04:18 +0100 Subject: [PATCH 05/10] Temporary remove autoDropAfterRelease toggle support --- .../codearte/gradle/nexus/PromoteRepositoryTask.groovy | 3 +-- .../gradle/nexus/logic/RepositoryPromoter.groovy | 10 +--------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy b/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy index 98e9d07..e4d3f81 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/PromoteRepositoryTask.groovy @@ -20,8 +20,7 @@ class PromoteRepositoryTask extends BaseStagingTask { String stagingProfileId = fetchAndCacheStagingProfileId(stagingProfileFetcher) String repositoryId = getRepositoryIdFromCloseTaskOrFromServer(stagingProfileId, repositoryFetcher) - def autoDropAfterRelease = Boolean.valueOf(project.properties["nexus-staging.autoDropAfterRelease"] as String) //TODO: Remove - repositoryPromoter.promoteRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId, autoDropAfterRelease) + repositoryPromoter.promoteRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId) } private void tryToTakeStagingProfileIdFromCloseRepositoryTask() { diff --git a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy index b2eb26d..47c5a8b 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy @@ -9,18 +9,10 @@ import groovy.util.logging.Slf4j @Slf4j class RepositoryPromoter extends AbstractStagingOperationExecutor { - void promoteRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId, boolean autoDropAfterRelease) { + void promoteRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) { log.info("Promoting repository '$repositoryId' with staging profile '$stagingProfileId'") Map postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId) - if (autoDropAfterRelease) { - Map data = postContent.data as Map - data.autoDropAfterRelease = true - } client.post(nexusUrl + "/staging/profiles/$stagingProfileId/promote", postContent) log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been promoted") } - - void promoteRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) { - promoteRepositoryWithIdAndStagingProfileId(repositoryId, stagingProfileId, false) - } } From 5fee35607ad29bd8739c5e056b42765c33081700 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Sat, 18 Mar 2017 23:16:12 +0100 Subject: [PATCH 06/10] Extract semi-manual E2E functional tests to separate class Also switched to dedicated AT user. --- .../gradle/nexus/CloseRepositoryTask.groovy | 1 - .../AbstractStagingOperationExecutor.groovy | 2 +- .../nexus/logic/RepositoryCloser.groovy | 2 +- .../nexus/logic/RepositoryDropper.groovy | 20 +++ .../nexus/logic/RepositoryPromoter.groovy | 2 +- .../nexus/FunctionalTestConstants.groovy | 9 ++ .../nexus/FunctionalTestHelperTrait.groovy | 20 +++ .../codearte/gradle/nexus/PasswordUtil.groovy | 12 -- .../BaseNexusStagingFunctionalSpec.groovy | 8 +- .../functional/BasicFunctionalSpec.groovy | 12 +- .../functional/E2EExperimentalSpec.groovy | 116 ++++++++++++++++++ .../logic/BaseOperationExecutorSpec.groovy | 2 - .../nexus/logic/RepositoryCloserSpec.groovy | 14 --- .../nexus/logic/RepositoryFetcherSpec.groovy | 29 ++--- .../nexus/logic/RepositoryPromoterSpec.groovy | 14 --- .../logic/StagingProfileFetcherSpec.groovy | 18 +-- 16 files changed, 189 insertions(+), 92 deletions(-) create mode 100644 src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryDropper.groovy create mode 100644 src/test/groovy/io/codearte/gradle/nexus/FunctionalTestConstants.groovy create mode 100644 src/test/groovy/io/codearte/gradle/nexus/FunctionalTestHelperTrait.groovy delete mode 100644 src/test/groovy/io/codearte/gradle/nexus/PasswordUtil.groovy create mode 100644 src/test/groovy/io/codearte/gradle/nexus/functional/E2EExperimentalSpec.groovy diff --git a/src/main/groovy/io/codearte/gradle/nexus/CloseRepositoryTask.groovy b/src/main/groovy/io/codearte/gradle/nexus/CloseRepositoryTask.groovy index c770e22..5aaf6ec 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/CloseRepositoryTask.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/CloseRepositoryTask.groovy @@ -5,7 +5,6 @@ import io.codearte.gradle.nexus.logic.OperationRetrier import io.codearte.gradle.nexus.logic.RepositoryCloser import io.codearte.gradle.nexus.logic.RepositoryFetcher import io.codearte.gradle.nexus.logic.StagingProfileFetcher -import org.gradle.api.internal.tasks.options.Option import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction diff --git a/src/main/groovy/io/codearte/gradle/nexus/logic/AbstractStagingOperationExecutor.groovy b/src/main/groovy/io/codearte/gradle/nexus/logic/AbstractStagingOperationExecutor.groovy index c9c110b..77473f3 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/logic/AbstractStagingOperationExecutor.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/logic/AbstractStagingOperationExecutor.groovy @@ -7,7 +7,7 @@ import groovy.transform.InheritConstructors @CompileStatic abstract class AbstractStagingOperationExecutor extends BaseOperationExecutor { - protected Map prepareStagingPostContentWithGivenRepositoryIdAndStagingId(String repositoryId, String stagingProfileId) { + protected Map prepareStagingPostContentWithGivenRepositoryIdAndStagingId(String repositoryId, String stagingProfileId) { return [data: [ stagedRepositoryId: repositoryId, description: 'Automatically released/promoted with gradle-nexus-staging-plugin!', diff --git a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryCloser.groovy b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryCloser.groovy index 0c2024a..81a2f7d 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryCloser.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryCloser.groovy @@ -11,7 +11,7 @@ class RepositoryCloser extends AbstractStagingOperationExecutor { void closeRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) { log.info("Closing repository '$repositoryId' with staging profile '$stagingProfileId'") - Map postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId) + Map postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId) client.post(nexusUrl + "/staging/profiles/$stagingProfileId/finish", postContent) log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been closed") } diff --git a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryDropper.groovy b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryDropper.groovy new file mode 100644 index 0000000..0fa90e7 --- /dev/null +++ b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryDropper.groovy @@ -0,0 +1,20 @@ +package io.codearte.gradle.nexus.logic + +import groovy.transform.CompileStatic +import groovy.transform.InheritConstructors +import groovy.util.logging.Slf4j +import org.gradle.api.Incubating + +@CompileStatic +@InheritConstructors +@Slf4j +@Incubating +class RepositoryDropper extends AbstractStagingOperationExecutor { + + void dropRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) { + log.info("Droping repository '$repositoryId' with staging profile '$stagingProfileId'") + Map postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId) + client.post(nexusUrl + "/staging/profiles/$stagingProfileId/drop", postContent) + log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been dropped") + } +} diff --git a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy index 47c5a8b..3e6503a 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoter.groovy @@ -11,7 +11,7 @@ class RepositoryPromoter extends AbstractStagingOperationExecutor { void promoteRepositoryWithIdAndStagingProfileId(String repositoryId, String stagingProfileId) { log.info("Promoting repository '$repositoryId' with staging profile '$stagingProfileId'") - Map postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId) + Map postContent = prepareStagingPostContentWithGivenRepositoryIdAndStagingId(repositoryId, stagingProfileId) client.post(nexusUrl + "/staging/profiles/$stagingProfileId/promote", postContent) log.info("Repository '$repositoryId' with staging profile '$stagingProfileId' has been promoted") } diff --git a/src/test/groovy/io/codearte/gradle/nexus/FunctionalTestConstants.groovy b/src/test/groovy/io/codearte/gradle/nexus/FunctionalTestConstants.groovy new file mode 100644 index 0000000..97092f0 --- /dev/null +++ b/src/test/groovy/io/codearte/gradle/nexus/FunctionalTestConstants.groovy @@ -0,0 +1,9 @@ +package io.codearte.gradle.nexus + +//Separate interface as there is problem with constants visibility in traits +interface FunctionalTestConstants { + + public static final String E2E_SERVER_BASE_PATH = "https://oss.sonatype.org/service/local/" + public static final String E2E_PACKAGE_GROUP = 'io.gitlab.nexus-at' + public static final String E2E_STAGING_PROFILE_ID = "5027d084a01a3a" +} diff --git a/src/test/groovy/io/codearte/gradle/nexus/FunctionalTestHelperTrait.groovy b/src/test/groovy/io/codearte/gradle/nexus/FunctionalTestHelperTrait.groovy new file mode 100644 index 0000000..ba6db92 --- /dev/null +++ b/src/test/groovy/io/codearte/gradle/nexus/FunctionalTestHelperTrait.groovy @@ -0,0 +1,20 @@ +package io.codearte.gradle.nexus + +import groovy.transform.CompileStatic + +@CompileStatic +trait FunctionalTestHelperTrait implements FunctionalTestConstants { + + private static final String NEXUS_USERNAME_AT_ENVIRONMENT_VARIABLE_NAME = 'nexusUsernameAT' + private static final String NEXUS_PASSWORD_AT_ENVIRONMENT_VARIABLE_NAME = 'nexusPasswordAT' + + String getNexusUsernameAT() { + return System.getenv(NEXUS_USERNAME_AT_ENVIRONMENT_VARIABLE_NAME) ?: 'nexus-at' + } + + //Temporary hack to read nexus password in e2e tests + String tryToReadNexusPasswordAT() { + return System.getenv(NEXUS_PASSWORD_AT_ENVIRONMENT_VARIABLE_NAME) ?: { throw new RuntimeException( + "Nexus password for AT tests is not set in a system variable '$NEXUS_PASSWORD_AT_ENVIRONMENT_VARIABLE_NAME'") }() + } +} diff --git a/src/test/groovy/io/codearte/gradle/nexus/PasswordUtil.groovy b/src/test/groovy/io/codearte/gradle/nexus/PasswordUtil.groovy deleted file mode 100644 index 2885fa2..0000000 --- a/src/test/groovy/io/codearte/gradle/nexus/PasswordUtil.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package io.codearte.gradle.nexus - -import groovy.transform.CompileStatic - -@CompileStatic -class PasswordUtil { - - //Temporary hack to read nexus password in e2e tests - static String tryToReadNexusPassword() { - return System.getenv("nexusPassword") ?: { throw new RuntimeException("Nexus password is not set in a system variable") }() - } -} diff --git a/src/test/groovy/io/codearte/gradle/nexus/functional/BaseNexusStagingFunctionalSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/functional/BaseNexusStagingFunctionalSpec.groovy index 0c81ef0..97be64c 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/functional/BaseNexusStagingFunctionalSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/functional/BaseNexusStagingFunctionalSpec.groovy @@ -5,10 +5,14 @@ import nebula.test.IntegrationSpec class BaseNexusStagingFunctionalSpec extends IntegrationSpec { protected String nexusPassword + protected String nexusUsername + protected String packageGroup void setup() { fork = true //to prevent ClassCastException: org.apache.xerces.parsers.XIncludeAwareParserConfiguration cannot be cast to org.apache.xerces.xni.parser.XMLParserConfiguration + nexusUsername = 'nexus-at' nexusPassword = '' + packageGroup = 'io.gitlab.nexus-at' } protected String getApplyPluginBlock() { @@ -26,9 +30,9 @@ class BaseNexusStagingFunctionalSpec extends IntegrationSpec { protected String getDefaultConfigurationClosure() { return """ nexusStaging { - username = "codearte" + username = '$nexusUsername' password = '$nexusPassword' - packageGroup = "io.codearte" + packageGroup = '$packageGroup' } """ } diff --git a/src/test/groovy/io/codearte/gradle/nexus/functional/BasicFunctionalSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/functional/BasicFunctionalSpec.groovy index 02f71f3..79ef465 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/functional/BasicFunctionalSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/functional/BasicFunctionalSpec.groovy @@ -1,17 +1,17 @@ package io.codearte.gradle.nexus.functional -import io.codearte.gradle.nexus.PasswordUtil +import io.codearte.gradle.nexus.FunctionalTestHelperTrait import spock.lang.Ignore import spock.lang.IgnoreIf -class BasicFunctionalSpec extends BaseNexusStagingFunctionalSpec { +class BasicFunctionalSpec extends BaseNexusStagingFunctionalSpec implements FunctionalTestHelperTrait { @Override void setup() { - nexusPassword = PasswordUtil.tryToReadNexusPassword() + nexusPassword = tryToReadNexusPasswordAT() } - @IgnoreIf({ !env.containsKey("nexusPassword") }) + @IgnoreIf({ !env.containsKey("nexusPasswordAT") }) def "should run"() { given: buildFile << """ @@ -24,7 +24,7 @@ class BasicFunctionalSpec extends BaseNexusStagingFunctionalSpec { result.wasExecuted(':getStagingProfile') and: // println result.standardOutput //TODO: How to redirect stdout to show on console (works with 2.2.1) - result.standardOutput.contains("Received staging profile id: 93c08fdebde1ff") + result.standardOutput.contains("Received staging profile id: $E2E_STAGING_PROFILE_ID") } @Ignore @@ -56,4 +56,4 @@ class BasicFunctionalSpec extends BaseNexusStagingFunctionalSpec { and: result.standardOutput.contains("has been promoted") //TODO: Match with regexp } -} \ No newline at end of file +} diff --git a/src/test/groovy/io/codearte/gradle/nexus/functional/E2EExperimentalSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/functional/E2EExperimentalSpec.groovy new file mode 100644 index 0000000..bf48b78 --- /dev/null +++ b/src/test/groovy/io/codearte/gradle/nexus/functional/E2EExperimentalSpec.groovy @@ -0,0 +1,116 @@ +package io.codearte.gradle.nexus.functional + +import groovy.transform.NotYetImplemented +import groovyx.net.http.RESTClient +import io.codearte.gradle.nexus.FunctionalTestHelperTrait +import io.codearte.gradle.nexus.infra.SimplifiedHttpJsonRestClient +import io.codearte.gradle.nexus.infra.WrongNumberOfRepositories +import io.codearte.gradle.nexus.logic.RepositoryCloser +import io.codearte.gradle.nexus.logic.RepositoryDropper +import io.codearte.gradle.nexus.logic.RepositoryFetcher +import io.codearte.gradle.nexus.logic.RepositoryPromoter +import io.codearte.gradle.nexus.logic.StagingProfileFetcher +import spock.lang.Ignore +import spock.lang.Specification +import spock.lang.Stepwise + +//TODO: Duplication with BasicFunctionalSpec done at Gradle level - decide which tests are better/easier to use and maintain +@Stepwise +@Ignore +class E2EExperimentalSpec extends Specification implements FunctionalTestHelperTrait { + + private SimplifiedHttpJsonRestClient client + + private static String resolvedStagingRepositoryId + + def setup() { + client = new SimplifiedHttpJsonRestClient(new RESTClient(), getNexusUsernameAT(), tryToReadNexusPasswordAT()) + } + + @NotYetImplemented + def "remove all staging repositories if exist as clean up"() {} + + def "should get staging profile id from server e2e"() { + given: + StagingProfileFetcher fetcher = new StagingProfileFetcher(client, E2E_SERVER_BASE_PATH) + when: + String stagingProfileId = fetcher.getStagingProfileIdForPackageGroup(E2E_PACKAGE_GROUP) + then: + stagingProfileId == E2E_STAGING_PROFILE_ID + } + + @NotYetImplemented + def "should upload artifacts to server"() {} + + def "should get open repository id from server e2e"() { + given: + RepositoryFetcher fetcher = new RepositoryFetcher(client, E2E_SERVER_BASE_PATH) + when: + String stagingRepositoryId = fetcher.getOpenRepositoryIdForStagingProfileId(E2E_STAGING_PROFILE_ID) + then: + println stagingRepositoryId + stagingRepositoryId.startsWith("iogitlabnexus-at") + and: + propagateStagingRepositoryIdToAnotherTest(stagingRepositoryId) + } + + def "should close open repository e2e"() { + given: + assert resolvedStagingRepositoryId + and: + RepositoryCloser closer = new RepositoryCloser(client, E2E_SERVER_BASE_PATH) + RepositoryFetcher fetcher = new RepositoryFetcher(client, E2E_SERVER_BASE_PATH) + when: + closer.closeRepositoryWithIdAndStagingProfileId(resolvedStagingRepositoryId, E2E_STAGING_PROFILE_ID) + then: + noExceptionThrown() + when: + waitForOperationToFinish() + and: + String closedRepositoryId = fetcher.getClosedRepositoryIdForStagingProfileId(E2E_STAGING_PROFILE_ID) + then: + closedRepositoryId == resolvedStagingRepositoryId + } + + @Ignore + def "should drop open repository e2e"() { + given: + assert resolvedStagingRepositoryId + and: + RepositoryDropper dropper = new RepositoryDropper(client, E2E_SERVER_BASE_PATH) + RepositoryFetcher fetcher = new RepositoryFetcher(client, E2E_SERVER_BASE_PATH) + when: + dropper.dropRepositoryWithIdAndStagingProfileId(resolvedStagingRepositoryId, E2E_STAGING_PROFILE_ID) + then: + noExceptionThrown() + when: + waitForOperationToFinish() + and: + fetcher.getOpenRepositoryIdForStagingProfileId(E2E_STAGING_PROFILE_ID) + then: + WrongNumberOfRepositories e = thrown() + e.numberOfRepositories == 0 + } + + def "should promote closed repository e2e"() { + given: + assert resolvedStagingRepositoryId + and: + RepositoryPromoter promoter = new RepositoryPromoter(client, E2E_SERVER_BASE_PATH) + when: + promoter.promoteRepositoryWithIdAndStagingProfileId(resolvedStagingRepositoryId, E2E_STAGING_PROFILE_ID) + then: + noExceptionThrown() + } + + @NotYetImplemented + def "repository after promotion should be dropped immediately"() {} + + private void propagateStagingRepositoryIdToAnotherTest(String stagingRepositoryId) { + resolvedStagingRepositoryId = stagingRepositoryId + } + + private void waitForOperationToFinish() { + sleep(6000) //TODO: until waiting for "transition complete" is not implemented - https://github.com/Codearte/gradle-nexus-staging-plugin/issues/21 + } +} diff --git a/src/test/groovy/io/codearte/gradle/nexus/logic/BaseOperationExecutorSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/logic/BaseOperationExecutorSpec.groovy index 42dadad..2c5327e 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/logic/BaseOperationExecutorSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/logic/BaseOperationExecutorSpec.groovy @@ -7,6 +7,4 @@ abstract class BaseOperationExecutorSpec extends Specification { protected static final String TEST_STAGING_PROFILE_ID = "93c08fdebde1ff" protected static final String TEST_REPOSITORY_ID = "iocodearte-1011" protected static final String MOCK_SERVER_HOST = "https://mock.server" - - protected static final String E2E_TEST_SERVER_BASE_PATH = "https://oss.sonatype.org/service/local/" } \ No newline at end of file diff --git a/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryCloserSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryCloserSpec.groovy index d989fd1..a879721 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryCloserSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryCloserSpec.groovy @@ -1,27 +1,13 @@ package io.codearte.gradle.nexus.logic import groovy.json.JsonSlurper -import groovyx.net.http.RESTClient -import io.codearte.gradle.nexus.PasswordUtil import io.codearte.gradle.nexus.infra.SimplifiedHttpJsonRestClient -import spock.lang.Ignore class RepositoryCloserSpec extends BaseOperationExecutorSpec { private static final String CLOSE_REPOSITORY_PATH = "/staging/profiles/$TEST_STAGING_PROFILE_ID/finish" private static final String CLOSE_REPOSITORY_FULL_URL = MOCK_SERVER_HOST + CLOSE_REPOSITORY_PATH - @Ignore - def "should close open repository e2e"() { - given: - def client = new SimplifiedHttpJsonRestClient(new RESTClient(), "codearte", PasswordUtil.tryToReadNexusPassword()) - def closer = new RepositoryCloser(client, E2E_TEST_SERVER_BASE_PATH) - when: - closer.closeRepositoryWithIdAndStagingProfileId(TEST_REPOSITORY_ID, TEST_STAGING_PROFILE_ID) - then: - noExceptionThrown() - } - def "should close open repository"() { given: def client = Mock(SimplifiedHttpJsonRestClient) diff --git a/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcherSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcherSpec.groovy index 37104e2..b780584 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcherSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryFetcherSpec.groovy @@ -1,12 +1,10 @@ package io.codearte.gradle.nexus.logic -import groovyx.net.http.RESTClient -import io.codearte.gradle.nexus.PasswordUtil +import io.codearte.gradle.nexus.FunctionalTestHelperTrait import io.codearte.gradle.nexus.infra.SimplifiedHttpJsonRestClient import io.codearte.gradle.nexus.infra.WrongNumberOfRepositories -import spock.lang.Ignore -class RepositoryFetcherSpec extends BaseOperationExecutorSpec implements FetcherResponseTrait { +class RepositoryFetcherSpec extends BaseOperationExecutorSpec implements FetcherResponseTrait, FunctionalTestHelperTrait { private static final String GET_REPOSITORY_ID_PATH = "/staging/profile_repositories/" private static final String GET_REPOSITORY_ID_FULL_URL = MOCK_SERVER_HOST + GET_REPOSITORY_ID_PATH + TEST_STAGING_PROFILE_ID @@ -19,18 +17,6 @@ class RepositoryFetcherSpec extends BaseOperationExecutorSpec implements Fetcher fetcher = new RepositoryFetcher(client, MOCK_SERVER_HOST) } - @Ignore - def "should get open repository id from server e2e"() { - given: - client = new SimplifiedHttpJsonRestClient(new RESTClient(), "codearte", PasswordUtil.tryToReadNexusPassword()) - fetcher = new RepositoryFetcher(client, E2E_TEST_SERVER_BASE_PATH) - when: - String stagingProfileId = fetcher.getOpenRepositoryIdForStagingProfileId(TEST_STAGING_PROFILE_ID) - then: - println stagingProfileId - stagingProfileId == TEST_REPOSITORY_ID - } - def "should get open repository id from server"() { given: client.get(GET_REPOSITORY_ID_FULL_URL) >> { createResponseMapWithGivenRepos([anOpenRepo()]) } @@ -86,13 +72,12 @@ class RepositoryFetcherSpec extends BaseOperationExecutorSpec implements Fetcher when: fetcher."get${expectedState.capitalize()}RepositoryIdForStagingProfileId"(TEST_STAGING_PROFILE_ID) then: - def e = thrown(WrongNumberOfRepositories) - e.message == "Wrong number of received repositories in state '$expectedState'. Expected 1, received 0" + def e = thrown(WrongNumberOfRepositories) + e.message == "Wrong number of received repositories in state '$expectedState'. Expected 1, received 0".toString() where: - - expectedState | receivedState - "open" | "closed" - "closed" | "open" + expectedState | receivedState + "open" | "closed" + "closed" | "open" } private Map anOpenRepo() { diff --git a/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoterSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoterSpec.groovy index 7246c03..921010d 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoterSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/logic/RepositoryPromoterSpec.groovy @@ -1,27 +1,13 @@ package io.codearte.gradle.nexus.logic import groovy.json.JsonSlurper -import groovyx.net.http.RESTClient -import io.codearte.gradle.nexus.PasswordUtil import io.codearte.gradle.nexus.infra.SimplifiedHttpJsonRestClient -import spock.lang.Ignore class RepositoryPromoterSpec extends BaseOperationExecutorSpec { private static final String PROMOTE_REPOSITORY_PATH = "/staging/profiles/$TEST_STAGING_PROFILE_ID/promote" private static final String PROMOTE_REPOSITORY_FULL_URL = MOCK_SERVER_HOST + PROMOTE_REPOSITORY_PATH - @Ignore - def "should promote repository e2e"() { - given: - def client = new SimplifiedHttpJsonRestClient(new RESTClient(), "codearte", PasswordUtil.tryToReadNexusPassword()) - def closer = new RepositoryPromoter(client, E2E_TEST_SERVER_BASE_PATH) - when: - closer.promoteRepositoryWithIdAndStagingProfileId(TEST_REPOSITORY_ID, TEST_STAGING_PROFILE_ID) - then: - noExceptionThrown() - } - def "should promote repository"() { given: def client = Mock(SimplifiedHttpJsonRestClient) diff --git a/src/test/groovy/io/codearte/gradle/nexus/logic/StagingProfileFetcherSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/logic/StagingProfileFetcherSpec.groovy index be08e54..cb2d0cf 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/logic/StagingProfileFetcherSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/logic/StagingProfileFetcherSpec.groovy @@ -1,29 +1,15 @@ package io.codearte.gradle.nexus.logic import groovy.json.JsonSlurper -import groovyx.net.http.RESTClient +import io.codearte.gradle.nexus.FunctionalTestHelperTrait import io.codearte.gradle.nexus.infra.SimplifiedHttpJsonRestClient -import io.codearte.gradle.nexus.PasswordUtil import io.codearte.gradle.nexus.infra.WrongNumberOfStagingProfiles -import spock.lang.Ignore -class StagingProfileFetcherSpec extends BaseOperationExecutorSpec { +class StagingProfileFetcherSpec extends BaseOperationExecutorSpec implements FunctionalTestHelperTrait { private static final String GET_STAGING_PROFILES_PATH = "/staging/profiles" private static final String GET_STAGING_PROFILES_FULL_URL = MOCK_SERVER_HOST + GET_STAGING_PROFILES_PATH - @Ignore - def "should get staging profile id from server e2e"() { - given: - def client = new SimplifiedHttpJsonRestClient(new RESTClient(), "codearte", PasswordUtil.tryToReadNexusPassword()) - def fetcher = new StagingProfileFetcher(client, E2E_TEST_SERVER_BASE_PATH) - when: - String stagingProfileId = fetcher.getStagingProfileIdForPackageGroup("io.codearte") - then: - println stagingProfileId - stagingProfileId == TEST_STAGING_PROFILE_ID - } - def "should get staging profile id from server"() { given: def client = Mock(SimplifiedHttpJsonRestClient) From c66610047b8a08b2e65660fcb2a9b5137c24fb90 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Sat, 18 Mar 2017 23:27:31 +0100 Subject: [PATCH 07/10] Reduce duplication in tests --- .../functional/MockedFunctionalSpec.groovy | 75 ++++++++----------- 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/src/test/groovy/io/codearte/gradle/nexus/functional/MockedFunctionalSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/functional/MockedFunctionalSpec.groovy index 01ec4e8..0d0701c 100644 --- a/src/test/groovy/io/codearte/gradle/nexus/functional/MockedFunctionalSpec.groovy +++ b/src/test/groovy/io/codearte/gradle/nexus/functional/MockedFunctionalSpec.groovy @@ -4,6 +4,7 @@ import com.github.tomakehurst.wiremock.junit.WireMockRule import com.github.tomakehurst.wiremock.stubbing.Scenario import groovy.json.JsonOutput import io.codearte.gradle.nexus.logic.FetcherResponseTrait +import nebula.test.functional.ExecutionResult import org.gradle.api.logging.LogLevel import org.junit.Rule @@ -41,7 +42,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet } """.stripIndent() when: - def result = runTasksSuccessfully(testedTaskName) + ExecutionResult result = runTasksSuccessfully(testedTaskName) then: result.wasExecuted(testedTaskName) result.standardOutput.contains("Using configured staging profile id: $stagingProfileId") @@ -87,7 +88,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet given: stubGetStagingProfilesWithJson(this.getClass().getResource("/io/codearte/gradle/nexus/logic/2stagingProfilesShrunkResponse.json").text) and: - stubGetOneOpenRepositoryAndOneClosedInFirstCallAndTwoClosedInTheNext(stagingProfileId) + stubGetOneOpenRepositoryAndOneClosedInFirstCallAndTwoClosedInTheNext(stagingProfileId) and: stubSuccessfulCloseRepositoryWithProfileId(stagingProfileId) and: @@ -98,12 +99,12 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet ${getDefaultConfigurationClosure()} """.stripIndent() when: - def result = runTasksSuccessfully("closeRepository", "promoteRepository") + ExecutionResult result = runTasksSuccessfully("closeRepository", "promoteRepository") then: result.wasExecuted("closeRepository") result.wasExecuted("promoteRepository") and: - verify(1, getRequestedFor(urlEqualTo("/staging/profile_repositories/$stagingProfileId"))) + verify(1, getRequestedFor(urlEqualTo("/staging/profile_repositories/$stagingProfileId"))) verify(1, getRequestedFor(urlEqualTo("/staging/profiles"))) } @@ -140,7 +141,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet } """.stripIndent() when: - def result = runTasksSuccessfully("promoteRepository") + ExecutionResult result = runTasksSuccessfully("promoteRepository") then: result.wasExecuted("promoteRepository") result.standardOutput.contains("Attempt 1/3 failed.") @@ -158,14 +159,14 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet and: logLevel = LogLevel.LIFECYCLE when: - def result = runTasksSuccessfully('getStagingProfile') + ExecutionResult result = runTasksSuccessfully('getStagingProfile') then: result.standardOutput.contains("Received staging profile id: 93c08fdebde1ff") } def "should call close and promote in closeAndPromoteRepository task"() { given: - stubGetOneOpenRepositoryAndOneClosedInFirstCallAndTwoClosedInTheNext(stagingProfileId) + stubGetOneOpenRepositoryAndOneClosedInFirstCallAndTwoClosedInTheNext(stagingProfileId) and: stubSuccessfulCloseRepositoryWithProfileId(stagingProfileId) and: @@ -179,7 +180,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet } """.stripIndent() when: - def result = runTasksSuccessfully("closeAndPromoteRepository") + ExecutionResult result = runTasksSuccessfully("closeAndPromoteRepository") then: result.wasExecuted("closeRepository") result.wasExecuted("promoteRepository") @@ -198,7 +199,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet project.group = "io.codearte" """.stripIndent() when: - def result = runTasksSuccessfully('getStagingProfile') + ExecutionResult result = runTasksSuccessfully('getStagingProfile') then: result.standardOutput.contains("Received staging profile id: 93c08fdebde1ff") } @@ -213,7 +214,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet project.group = "io.someother" """.stripIndent() when: - def result = runTasksSuccessfully('getStagingProfile') + ExecutionResult result = runTasksSuccessfully('getStagingProfile') then: result.standardOutput.contains("Received staging profile id: 93c08fdebde1ff") } @@ -250,16 +251,15 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet } private void stubSuccessfulCloseRepositoryWithProfileId(String stagingProfileId) { - stubFor(post(urlEqualTo("/staging/profiles/$stagingProfileId/finish")) - .withHeader("Content-Type", equalTo("application/json")) - .withHeader("Accept", containing("application/json")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json"))) + stubGivenSuccessfulTransitionOperationWithProfileId("finish", stagingProfileId) } private void stubSuccessfulPromoteRepositoryWithProfileId(String stagingProfileId) { - stubFor(post(urlEqualTo("/staging/profiles/$stagingProfileId/promote")) + stubGivenSuccessfulTransitionOperationWithProfileId("promote", stagingProfileId) + } + + private void stubGivenSuccessfulTransitionOperationWithProfileId(String restCommandName, String stagingProfileId) { + stubFor(post(urlEqualTo("/staging/profiles/$stagingProfileId/$restCommandName")) .withHeader("Content-Type", equalTo("application/json")) .withHeader("Accept", containing("application/json")) .willReturn(aResponse() @@ -268,34 +268,19 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet } private void stubGetOneOpenRepositoryAndOneClosedInFirstCallAndTwoClosedInTheNext(String stagingProfileId) { - stubFor(get(urlEqualTo("/staging/profile_repositories/$stagingProfileId")).inScenario("State") - .whenScenarioStateIs(Scenario.STARTED) - .withHeader("Content-Type", containing("application/json")) - .withHeader("Accept", containing("application/json")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody( - JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("open", "ignored"), aRepoInStateAndId("closed", "ignoredClosed")])) - ) - ) - .willSetStateTo("CLOSED")) - - stubFor(get(urlEqualTo("/staging/profile_repositories/$stagingProfileId")).inScenario("State") - .whenScenarioStateIs("CLOSED") - .withHeader("Content-Type", containing("application/json")) - .withHeader("Accept", containing("application/json")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody( - JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("closed", "ignored"), aRepoInStateAndId("closed", "ignoredClosed")])) - ) - ) - ) + stubGetGivenRepositoriesInFirstAndSecondCall(stagingProfileId, + [aRepoInStateAndId("open", "ignored"), aRepoInStateAndId("closed", "ignoredClosed")], + [aRepoInStateAndId("closed", "ignored"), aRepoInStateAndId("closed", "ignoredClosed")]) } private void stubGetOneOpenRepositoryInFirstCallAndOneClosedInTheNext(String stagingProfileId) { + stubGetGivenRepositoriesInFirstAndSecondCall(stagingProfileId, + [aRepoInStateAndId("open", "ignored")], + [aRepoInStateAndId("closed", "ignored")]) + } + + private void stubGetGivenRepositoriesInFirstAndSecondCall(String stagingProfileId, List repositoriesToReturnInFirstCall, + List repositoriesToReturnInSecondCall) { stubFor(get(urlEqualTo("/staging/profile_repositories/$stagingProfileId")).inScenario("State") .whenScenarioStateIs(Scenario.STARTED) .withHeader("Content-Type", containing("application/json")) @@ -304,7 +289,7 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet .withStatus(200) .withHeader("Content-Type", "application/json") .withBody( - JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("open", "ignored")])) + JsonOutput.toJson(createResponseMapWithGivenRepos(repositoriesToReturnInFirstCall)) ) ) .willSetStateTo("CLOSED")) @@ -316,7 +301,9 @@ class MockedFunctionalSpec extends BaseNexusStagingFunctionalSpec implements Fet .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") - .withBody(JsonOutput.toJson(createResponseMapWithGivenRepos([aRepoInStateAndId("closed", "ignored")]))) + .withBody( + JsonOutput.toJson(createResponseMapWithGivenRepos(repositoriesToReturnInSecondCall)) + ) ) ) } From adbacfbf992c7f534ea19382ed798fb5e707767f Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Sun, 19 Mar 2017 16:42:44 +0100 Subject: [PATCH 08/10] [#5] Improve error handling on POST Also covered by tests. --- .../infra/NexusHttpResponseException.groovy | 22 +++++++ .../infra/SimplifiedHttpJsonRestClient.groovy | 29 ++++---- .../MockedResponseErrorHandlingSpec.groovy | 66 +++++++++++++++++++ 3 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 src/main/groovy/io/codearte/gradle/nexus/infra/NexusHttpResponseException.groovy create mode 100644 src/test/groovy/io/codearte/gradle/nexus/functional/MockedResponseErrorHandlingSpec.groovy diff --git a/src/main/groovy/io/codearte/gradle/nexus/infra/NexusHttpResponseException.groovy b/src/main/groovy/io/codearte/gradle/nexus/infra/NexusHttpResponseException.groovy new file mode 100644 index 0000000..02e6f88 --- /dev/null +++ b/src/main/groovy/io/codearte/gradle/nexus/infra/NexusHttpResponseException.groovy @@ -0,0 +1,22 @@ +package io.codearte.gradle.nexus.infra + +import groovy.transform.CompileStatic + +/** + * Custom exception to propagate server errors. + * + * Created as groovyx.net.http.HttpResponseException contains in a message only a reason phrase (e.g. Server Error) without response body + * which in many cases is crucial to determine the resons why error was returned. + * + * It may be made redundant once migrated to other HTTP library. + */ +@CompileStatic +class NexusHttpResponseException extends NexusStagingException { + + final int statusCode + + NexusHttpResponseException(int statusCode, String message, Throwable cause) { + super(message, cause) + this.statusCode = statusCode + } +} diff --git a/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy b/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy index 9e557e6..616aed3 100644 --- a/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy +++ b/src/main/groovy/io/codearte/gradle/nexus/infra/SimplifiedHttpJsonRestClient.groovy @@ -1,11 +1,10 @@ package io.codearte.gradle.nexus.infra -import org.apache.http.client.HttpResponseException; - import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import groovyx.net.http.ContentType import groovyx.net.http.HttpResponseDecorator +import groovyx.net.http.HttpResponseException import groovyx.net.http.RESTClient /** @@ -52,19 +51,17 @@ class SimplifiedHttpJsonRestClient { setUriAndAuthentication(uri) Map params = createAndInitializeCallParametersMap() params.body = content - log.debug("POST request content: $content") - try { - //TODO: Add better error handling (e.g. display error message received from server, not only 500 + not fail on 404 in 'text/html') - HttpResponseDecorator response = (HttpResponseDecorator)restClient.post(params) - log.warn("POST response data: ${response.data}") - } catch(groovyx.net.http.HttpResponseException e) { - //Apache' HttpResponseException ONLY puts the 2nd param in the e.getMessage which will be printed so - //put all information there (status code, error str, body of response in case they put more error information there) - - HttpResponseDecorator resp = e.getResponse(); - String message = "${resp.statusLine.statusCode}:${resp.statusLine.reasonPhrase} body=${resp.data}" - log.error("POST response failed. ${message}") - throw new HttpResponseException(e.getStatusCode(), message) - } + try { + log.debug("POST request content: $content") + HttpResponseDecorator response = (HttpResponseDecorator) restClient.post(params) + log.debug("POST response status ${response.status}, data: ${response.data}") + } catch (HttpResponseException e) { + //Enhance rethrown exception to contain also response body - #5 + //TODO: Still better handle response content type on 404 and 50x - server returns 'text/plain', but RESTClient from Groovy Builder tries to parse it as JSON + HttpResponseDecorator resp = e.getResponse(); + String message = "${resp.statusLine.statusCode}: ${resp.statusLine.reasonPhrase}, body: ${resp.data}" + log.warn("POST response failed. ${message}") + throw new NexusHttpResponseException(e.getStatusCode(), message, e) + } } } diff --git a/src/test/groovy/io/codearte/gradle/nexus/functional/MockedResponseErrorHandlingSpec.groovy b/src/test/groovy/io/codearte/gradle/nexus/functional/MockedResponseErrorHandlingSpec.groovy new file mode 100644 index 0000000..78ec851 --- /dev/null +++ b/src/test/groovy/io/codearte/gradle/nexus/functional/MockedResponseErrorHandlingSpec.groovy @@ -0,0 +1,66 @@ +package io.codearte.gradle.nexus.functional + +import com.github.tomakehurst.wiremock.junit.WireMockRule +import groovy.transform.NotYetImplemented +import groovyx.net.http.HttpResponseException +import groovyx.net.http.RESTClient +import io.codearte.gradle.nexus.infra.NexusHttpResponseException +import io.codearte.gradle.nexus.infra.SimplifiedHttpJsonRestClient +import io.codearte.gradle.nexus.logic.RepositoryCloser +import org.junit.Rule +import spock.lang.Specification + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse +import static com.github.tomakehurst.wiremock.client.WireMock.containing +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo +import static com.github.tomakehurst.wiremock.client.WireMock.post +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo + +class MockedResponseErrorHandlingSpec extends Specification { + + private static final String TEST_MOCKED_USERNAME = '' + private static final String TEST_MOCKED_PASSWORD = '' + private static final String TEST_MOCKED_STAGING_PROFILE_ID = "93c08fdebde1ff" + private static final String TEST_MOCKED_NOT_EXISTING_REPOSITORY_ID = "xxx" + private static final String TEST_MOCKED_SERVER_ERROR_JSON_RESPONSE = """ + { + "errors": [ + { + "id": "*", + "msg": "Unhandled: Missing staging repository: $TEST_MOCKED_NOT_EXISTING_REPOSITORY_ID" + } + ] + } + """.stripIndent() + + @Rule + public WireMockRule wireMockRule = new WireMockRule(8089) + + //Using private Options as server is not started yet + private String mockedUrl = "http://localhost:${wireMockRule.options.portNumber()}/" + + def "should present response body on 500 server error"() { + given: + SimplifiedHttpJsonRestClient client = new SimplifiedHttpJsonRestClient(new RESTClient(), TEST_MOCKED_USERNAME, TEST_MOCKED_PASSWORD) + RepositoryCloser closer = new RepositoryCloser(client, mockedUrl) + and: + stubFor(post(urlEqualTo("/staging/profiles/$TEST_MOCKED_STAGING_PROFILE_ID/finish")) + .withHeader("Content-Type", equalTo("application/json")) + .withHeader("Accept", containing("application/json")) + .willReturn(aResponse() + .withStatus(500) + .withBody(TEST_MOCKED_SERVER_ERROR_JSON_RESPONSE) + .withHeader("Content-Type", "application/json"))) + when: + closer.closeRepositoryWithIdAndStagingProfileId(TEST_MOCKED_NOT_EXISTING_REPOSITORY_ID, TEST_MOCKED_STAGING_PROFILE_ID) + then: + NexusHttpResponseException e = thrown() + e.statusCode == 500 + e.message.contains("Missing staging repository: $TEST_MOCKED_NOT_EXISTING_REPOSITORY_ID") + e.cause instanceof HttpResponseException + } + + @NotYetImplemented + def "should present response body on 400 or 500 errors with plain text response"() {} +} From a19a5692a18db499cdb734cadb52e861622dedb0 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Sun, 19 Mar 2017 17:26:12 +0100 Subject: [PATCH 09/10] [#33] EditorConfig configuration To make it easier to contribute to the project. Fixes #33. --- .editorconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6f91ec7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# EditorConfig: http://EditorConfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.json] +indent_size = 2 From af3c9941c00632730a0223a1b69b1788b042f1b7 Mon Sep 17 00:00:00 2001 From: Marcin Zajaczkowski Date: Sun, 19 Mar 2017 21:51:49 +0100 Subject: [PATCH 10/10] Add CHANGELOG file GitHub releases are nice, but having changelog (also) in code repository makes me feel relaxed. --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1a22369 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,29 @@ +0.6.0 - 2017-03-19 + + - Consider state trying to find just one repository in given state - [#36](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/36) - contribution by [strelok1](https://github.com/strelok1) + - Better error message in case of HTTP request failure - [#5](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/5) - contribution by [deanhiller](https://github.com/deanhiller) + - Add EditorConfig configuration to better deal with spaces vs tabs - [#33](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/33) + +0.5.3 - 2015-06-13 + + - `packageGroup` should be taken from project.group by default - [#11](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/11) + +0.5.2 - 2015-06-09 + + - Provide single task to close and promote repository - [#9](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/9) + - `getStagingProfile` task should display output without `--info` switch - [#8](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/8) + +0.5.1 - 2015-03-08 + + - Credentials should be automatically fetched from configured deployer - [#7](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/7) + - Credentials should be automatically fetched from Gradle properties (when available) - [#6](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/6) + +0.5.0 - 2015-03-02 + + - Wait given time period when repositories are not yet available - [#3](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/3) + - Use configured stagingProfileId when available - [#2](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/2) + - nexusUrl by default should use Sonatype OSSRH - [#1](https://github.com/Codearte/gradle-nexus-staging-plugin/issues/1) + +0.4.0 - 2015-02-27 + + - Initial release