From 8649c26891eecd6dec97e78ede1ebdfb877a7524 Mon Sep 17 00:00:00 2001 From: ChristopherDancy Date: Sun, 13 Jan 2019 15:33:59 -0500 Subject: [PATCH 1/7] Initial impl of SynApi. --- .../cdancy/bitbucket/rest/BitbucketApi.java | 4 ++ .../{pullrequest => common}/Reference.java | 0 .../rest/fallbacks/BitbucketFallbacks.java | 22 ++++++++ .../bitbucket/rest/features/SyncApi.java | 53 +++++++++++++++++++ 4 files changed, 79 insertions(+) rename src/main/java/com/cdancy/bitbucket/rest/domain/{pullrequest => common}/Reference.java (100%) create mode 100644 src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java diff --git a/src/main/java/com/cdancy/bitbucket/rest/BitbucketApi.java b/src/main/java/com/cdancy/bitbucket/rest/BitbucketApi.java index 5d0a1ed4..5a422ec3 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/BitbucketApi.java +++ b/src/main/java/com/cdancy/bitbucket/rest/BitbucketApi.java @@ -30,6 +30,7 @@ import com.cdancy.bitbucket.rest.features.ProjectApi; import com.cdancy.bitbucket.rest.features.PullRequestApi; import com.cdancy.bitbucket.rest.features.RepositoryApi; +import com.cdancy.bitbucket.rest.features.SyncApi; import com.cdancy.bitbucket.rest.features.SystemApi; import com.cdancy.bitbucket.rest.features.TagApi; import com.cdancy.bitbucket.rest.features.TasksApi; @@ -71,6 +72,9 @@ public interface BitbucketApi extends Closeable { @Delegate RepositoryApi repositoryApi(); + @Delegate + SyncApi syncApi(); + @Delegate SystemApi systemApi(); diff --git a/src/main/java/com/cdancy/bitbucket/rest/domain/pullrequest/Reference.java b/src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java similarity index 100% rename from src/main/java/com/cdancy/bitbucket/rest/domain/pullrequest/Reference.java rename to src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java diff --git a/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java b/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java index cccee828..6b687ab9 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java +++ b/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java @@ -63,6 +63,7 @@ import com.cdancy.bitbucket.rest.domain.repository.PullRequestSettings; import com.cdancy.bitbucket.rest.domain.repository.Repository; import com.cdancy.bitbucket.rest.domain.repository.RepositoryPage; +import com.cdancy.bitbucket.rest.domain.sync.Enabled; import com.cdancy.bitbucket.rest.domain.tags.Tag; import com.cdancy.bitbucket.rest.domain.tags.TagPage; import com.google.common.collect.Lists; @@ -366,6 +367,23 @@ public Object createOrPropagate(final Throwable throwable) throws Exception { } } + public static final class EnabledOnError implements Fallback { + @Override + public Object createOrPropagate(final Throwable throwable) throws Exception { + if (checkNotNull(throwable, "throwable") != null) { + final Boolean is204 = returnValueOnCodeOrNull(throwable, true, equalTo(204)); + final boolean isAvailable = (is204 != null) ? true : false; + final List errors = getErrors(throwable.getMessage()); + if (errors.size() > 0 && errors.get(0).context().startsWith("Error parsing input: null")) { + return createEnabledFromErrors(isAvailable, null); + } else { + return createEnabledFromErrors(isAvailable, errors); + } + } + throw propagate(throwable); + } + } + public static final class ActivitiesPageOnError implements Fallback { @Override public Object createOrPropagate(final Throwable throwable) throws Exception { @@ -601,6 +619,10 @@ public static PullRequest createPullRequestFromErrors(final List errors) null, null, errors); } + public static Enabled createEnabledFromErrors(final boolean isAvailable, final List errors) { + return Enabled.create(isAvailable, false, null, null, null, null, errors); + } + public static ActivitiesPage createActivitiesPageFromErrors(final List errors) { return ActivitiesPage.create(-1, -1, -1, -1, true, null, errors); } diff --git a/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java b/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java new file mode 100644 index 00000000..b184178e --- /dev/null +++ b/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cdancy.bitbucket.rest.features; + +import com.cdancy.bitbucket.rest.annotations.Documentation; +import com.cdancy.bitbucket.rest.domain.sync.Enabled; +import com.cdancy.bitbucket.rest.fallbacks.BitbucketFallbacks; +import com.cdancy.bitbucket.rest.filters.BitbucketAuthenticationFilter; +import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.Payload; +import org.jclouds.rest.annotations.PayloadParam; +import org.jclouds.rest.annotations.RequestFilters; + +import javax.inject.Named; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Produces(MediaType.APPLICATION_JSON) +@RequestFilters(BitbucketAuthenticationFilter.class) +@Path("/rest/sync/{jclouds.api-version}/projects") +@SuppressWarnings("PMD.AvoidDuplicateLiterals") +public interface SyncApi { + + @Named("sync:enable") + @Documentation({"https://docs.atlassian.com/DAC/rest/stash/3.7.2/stash-repository-ref-sync-rest.html"}) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{project}/repos/{repo}") + @Payload("%7B \"enabled\": \"{enabled}\" %7D") + @Fallback(BitbucketFallbacks.EnabledOnError.class) + @POST + Enabled enable(@PathParam("project") String project, + @PathParam("repo") String repo, + @PayloadParam("enabled") boolean enabled); +} From b035bb2afa6ef22586185003643403df140f5d1e Mon Sep 17 00:00:00 2001 From: ChristopherDancy Date: Sun, 13 Jan 2019 15:35:10 -0500 Subject: [PATCH 2/7] Added mockTests for SyncApi.enable endpoint. --- .../rest/domain/common/Reference.java | 30 ++++-- .../rest/domain/pullrequest/PullRequest.java | 1 + .../bitbucket/rest/domain/sync/Enabled.java | 70 +++++++++++++ .../rest/options/CreatePullRequest.java | 4 +- .../rest/features/CommentsApiLiveTest.java | 2 +- .../rest/features/PullRequestApiLiveTest.java | 2 +- .../rest/features/PullRequestApiMockTest.java | 4 +- .../rest/features/SyncApiMockTest.java | 99 +++++++++++++++++++ .../rest/features/TasksApiLiveTest.java | 2 +- src/test/resources/sync-enabled.json | 15 +++ 10 files changed, 213 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/cdancy/bitbucket/rest/domain/sync/Enabled.java create mode 100644 src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java create mode 100644 src/test/resources/sync-enabled.json diff --git a/src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java b/src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java index 2ac7af83..d51c08ba 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java +++ b/src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java @@ -15,8 +15,9 @@ * limitations under the License. */ -package com.cdancy.bitbucket.rest.domain.pullrequest; +package com.cdancy.bitbucket.rest.domain.common; +import com.cdancy.bitbucket.rest.domain.pullrequest.MinimalRepository; import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; @@ -29,8 +30,15 @@ public abstract class Reference { @Nullable public abstract String id(); + @Nullable public abstract MinimalRepository repository(); + @Nullable + public abstract String state(); + + @Nullable + public abstract Boolean tag(); + @Nullable public abstract String displayId(); @@ -55,18 +63,22 @@ public static Reference create(final String id, final MinimalRepository repository, final String displayId) { - return create(id, repository, displayId, null); + return create(id, repository, null, null, displayId, null); } - @SerializedNames({"id", "repository", "displayId", "latestCommit"}) - public static Reference create(final String id, - final MinimalRepository repository, - final String displayId, + @SerializedNames({"id", "repository", "state", "tag", "displayId", "latestCommit"}) + public static Reference create(final String id, + final MinimalRepository repository, + final String state, + final Boolean tag, + final String displayId, final String latestCommit) { - return new AutoValue_Reference(id != null ? id : "refs/heads/master", - repository, - displayId, + return new AutoValue_Reference(id != null ? id : "refs/heads/master", + repository, + state, + tag, + displayId, latestCommit); } } diff --git a/src/main/java/com/cdancy/bitbucket/rest/domain/pullrequest/PullRequest.java b/src/main/java/com/cdancy/bitbucket/rest/domain/pullrequest/PullRequest.java index a1a4e1e1..faa42d50 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/domain/pullrequest/PullRequest.java +++ b/src/main/java/com/cdancy/bitbucket/rest/domain/pullrequest/PullRequest.java @@ -19,6 +19,7 @@ import java.util.List; +import com.cdancy.bitbucket.rest.domain.common.Reference; import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; diff --git a/src/main/java/com/cdancy/bitbucket/rest/domain/sync/Enabled.java b/src/main/java/com/cdancy/bitbucket/rest/domain/sync/Enabled.java new file mode 100644 index 00000000..fc4bb890 --- /dev/null +++ b/src/main/java/com/cdancy/bitbucket/rest/domain/sync/Enabled.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cdancy.bitbucket.rest.domain.sync; + +import com.cdancy.bitbucket.rest.BitbucketUtils; +import com.cdancy.bitbucket.rest.domain.common.Error; +import com.cdancy.bitbucket.rest.domain.common.ErrorsHolder; +import com.cdancy.bitbucket.rest.domain.common.Reference; +import com.google.auto.value.AutoValue; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +import java.util.List; + +@SuppressWarnings("PMD") +@AutoValue +public abstract class Enabled implements ErrorsHolder { + + @Nullable + public abstract Boolean available(); + + @Nullable + public abstract Boolean enabled(); + + @Nullable + public abstract Long lastSync(); + + public abstract List aheadRefs(); + + public abstract List divergedRefs(); + + public abstract List orphanedRefs(); + + Enabled() { + } + + @SerializedNames({ "available", "enabled", "lastSync", + "aheadRefs", "divergedRefs", "orphanedRefs", "errors" }) + public static Enabled create(final Boolean available, + final Boolean enabled, + final Long lastSync, + final List aheadRefs, + final List divergedRefs, + final List orphanedRefs, + final List errors) { + + return new AutoValue_Enabled(BitbucketUtils.nullToEmpty(errors), + available != null ? available : true, + enabled != null ? enabled : false, + lastSync != null ? lastSync : 0, + BitbucketUtils.nullToEmpty(aheadRefs), + BitbucketUtils.nullToEmpty(divergedRefs), + BitbucketUtils.nullToEmpty(orphanedRefs)); + } +} diff --git a/src/main/java/com/cdancy/bitbucket/rest/options/CreatePullRequest.java b/src/main/java/com/cdancy/bitbucket/rest/options/CreatePullRequest.java index 2f8a7239..fd8eaacf 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/options/CreatePullRequest.java +++ b/src/main/java/com/cdancy/bitbucket/rest/options/CreatePullRequest.java @@ -19,7 +19,7 @@ import com.cdancy.bitbucket.rest.domain.common.Links; import com.cdancy.bitbucket.rest.domain.pullrequest.Person; -import com.cdancy.bitbucket.rest.domain.pullrequest.Reference; +import com.cdancy.bitbucket.rest.domain.common.Reference; import com.google.auto.value.AutoValue; import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; @@ -71,4 +71,4 @@ public static CreatePullRequest create(final String title, return new AutoValue_CreatePullRequest(title, description, "OPEN", true, false, fromRef, toRef, false, reviewers, links); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/CommentsApiLiveTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/CommentsApiLiveTest.java index 30849d57..7028fae6 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/CommentsApiLiveTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/CommentsApiLiveTest.java @@ -34,7 +34,7 @@ import com.cdancy.bitbucket.rest.domain.pullrequest.MinimalRepository; import com.cdancy.bitbucket.rest.domain.pullrequest.ProjectKey; import com.cdancy.bitbucket.rest.domain.pullrequest.PullRequest; -import com.cdancy.bitbucket.rest.domain.pullrequest.Reference; +import com.cdancy.bitbucket.rest.domain.common.Reference; import com.cdancy.bitbucket.rest.options.CreateComment; import com.cdancy.bitbucket.rest.options.CreatePullRequest; import com.google.common.collect.Lists; diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiLiveTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiLiveTest.java index 8f602537..0c2ec815 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiLiveTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiLiveTest.java @@ -29,7 +29,7 @@ import com.cdancy.bitbucket.rest.domain.commit.CommitPage; import com.cdancy.bitbucket.rest.domain.pullrequest.ProjectKey; import com.cdancy.bitbucket.rest.domain.pullrequest.PullRequest; -import com.cdancy.bitbucket.rest.domain.pullrequest.Reference; +import com.cdancy.bitbucket.rest.domain.common.Reference; import com.cdancy.bitbucket.rest.options.CreateParticipants; import com.cdancy.bitbucket.rest.options.CreatePullRequest; diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiMockTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiMockTest.java index f4ca03de..26d0e8a3 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiMockTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiMockTest.java @@ -30,7 +30,7 @@ import com.cdancy.bitbucket.rest.domain.pullrequest.ProjectKey; import com.cdancy.bitbucket.rest.domain.pullrequest.PullRequest; import com.cdancy.bitbucket.rest.domain.pullrequest.PullRequestPage; -import com.cdancy.bitbucket.rest.domain.pullrequest.Reference; +import com.cdancy.bitbucket.rest.domain.common.Reference; import com.cdancy.bitbucket.rest.domain.pullrequest.User; import com.cdancy.bitbucket.rest.options.CreateParticipants; import org.testng.annotations.Test; @@ -90,7 +90,7 @@ public void testCreatePullRequest() throws Exception { final MinimalRepository repository2 = MinimalRepository.create(repoKey, null, proj2); final String commitId = "930228bb501e07c2653771858320873d94518e33"; - final Reference fromRef = Reference.create("refs/heads/feature-ABC-123", repository1, "feature-ABC-123", commitId); + final Reference fromRef = Reference.create("refs/heads/feature-ABC-123", repository1, null, null, "feature-ABC-123", commitId); final Reference toRef = Reference.create("refs/heads/master", repository2); final CreatePullRequest cpr = CreatePullRequest.create("Talking Nerdy", "Some description", fromRef, toRef, null, null); final PullRequest pr = api.create(repository2.project().key(), repository2.slug(), cpr); diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java new file mode 100644 index 00000000..a24e63b4 --- /dev/null +++ b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cdancy.bitbucket.rest.features; + +import com.cdancy.bitbucket.rest.BaseBitbucketMockTest; +import com.cdancy.bitbucket.rest.BitbucketApi; +import com.cdancy.bitbucket.rest.BitbucketApiMetadata; +import com.cdancy.bitbucket.rest.domain.sync.Enabled; +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@Test(groups = "unit", testName = "SyncApiMockTest") +public class SyncApiMockTest extends BaseBitbucketMockTest { + + private final String restApiPath = "/rest/sync/"; + + private final String projectKey = "PRJ"; + private final String repoKey = "my-repo"; + private final String syncPath = "/projects/" + projectKey + "/repos/" + repoKey; + + public void testEnabled() throws Exception { + final MockWebServer server = mockWebServer(); + + server.enqueue(new MockResponse().setBody(payloadFromResource("/sync-enabled.json")).setResponseCode(200)); + final BitbucketApi baseApi = api(server.getUrl("/")); + final SyncApi api = baseApi.syncApi(); + try { + final Enabled status = api.enable(projectKey, repoKey, true); + assertThat(status.available()).isTrue(); + assertThat(status.enabled()).isTrue(); + assertThat(status.divergedRefs()).isNotEmpty(); + assertThat(status.divergedRefs().get(0).state()).isEqualTo("DIVERGED"); + assertThat(status.errors()).isEmpty(); + + assertSent(server, "POST", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); + } finally { + baseApi.close(); + server.shutdown(); + } + } + + public void testDisabled() throws Exception { + final MockWebServer server = mockWebServer(); + + server.enqueue(new MockResponse().setResponseCode(204)); + final BitbucketApi baseApi = api(server.getUrl("/")); + final SyncApi api = baseApi.syncApi(); + try { + final Enabled status = api.enable(projectKey, repoKey, true); + assertThat(status.available()).isTrue(); + assertThat(status.enabled()).isFalse(); + assertThat(status.divergedRefs()).isEmpty(); + assertThat(status.errors()).isEmpty(); + + assertSent(server, "POST", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); + } finally { + baseApi.close(); + server.shutdown(); + } + } + + public void testEnabledOnError() throws Exception { + final MockWebServer server = mockWebServer(); + + server.enqueue(new MockResponse().setBody(payloadFromResource("/errors.json")).setResponseCode(400)); + final BitbucketApi baseApi = api(server.getUrl("/")); + final SyncApi api = baseApi.syncApi(); + try { + final Enabled status = api.enable(projectKey, repoKey, true); + assertThat(status.available()).isFalse(); + assertThat(status.enabled()).isFalse(); + assertThat(status.divergedRefs()).isEmpty(); + assertThat(status.errors()).isNotEmpty(); + + assertSent(server, "POST", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); + } finally { + baseApi.close(); + server.shutdown(); + } + } +} diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/TasksApiLiveTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/TasksApiLiveTest.java index 58eab436..a8d3496d 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/TasksApiLiveTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/TasksApiLiveTest.java @@ -30,7 +30,7 @@ import com.cdancy.bitbucket.rest.domain.pullrequest.MinimalRepository; import com.cdancy.bitbucket.rest.domain.pullrequest.ProjectKey; import com.cdancy.bitbucket.rest.domain.pullrequest.PullRequest; -import com.cdancy.bitbucket.rest.domain.pullrequest.Reference; +import com.cdancy.bitbucket.rest.domain.common.Reference; import com.cdancy.bitbucket.rest.options.CreatePullRequest; import com.cdancy.bitbucket.rest.options.CreateTask; diff --git a/src/test/resources/sync-enabled.json b/src/test/resources/sync-enabled.json new file mode 100644 index 00000000..13e6cd04 --- /dev/null +++ b/src/test/resources/sync-enabled.json @@ -0,0 +1,15 @@ +{ + "available": true, + "enabled": true, + "lastSync": 1331038800000, + "aheadRefs": [], + "divergedRefs": [ + { + "id": "refs/heads/master", + "displayId": "master", + "state": "DIVERGED", + "tag": false + } + ], + "orphanedRefs": [] +} \ No newline at end of file From 5f32215468515b8b6c4268c85a028ef64b0a4c00 Mon Sep 17 00:00:00 2001 From: ChristopherDancy Date: Sun, 13 Jan 2019 20:14:26 -0500 Subject: [PATCH 3/7] SyncApi gained endpoint 'status' --- .../sync/{Enabled.java => SyncStatus.java} | 20 ++++++++-------- .../rest/fallbacks/BitbucketFallbacks.java | 12 +++++----- .../bitbucket/rest/features/SyncApi.java | 23 +++++++++++++++---- 3 files changed, 34 insertions(+), 21 deletions(-) rename src/main/java/com/cdancy/bitbucket/rest/domain/sync/{Enabled.java => SyncStatus.java} (77%) diff --git a/src/main/java/com/cdancy/bitbucket/rest/domain/sync/Enabled.java b/src/main/java/com/cdancy/bitbucket/rest/domain/sync/SyncStatus.java similarity index 77% rename from src/main/java/com/cdancy/bitbucket/rest/domain/sync/Enabled.java rename to src/main/java/com/cdancy/bitbucket/rest/domain/sync/SyncStatus.java index fc4bb890..dd8278c0 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/domain/sync/Enabled.java +++ b/src/main/java/com/cdancy/bitbucket/rest/domain/sync/SyncStatus.java @@ -29,7 +29,7 @@ @SuppressWarnings("PMD") @AutoValue -public abstract class Enabled implements ErrorsHolder { +public abstract class SyncStatus implements ErrorsHolder { @Nullable public abstract Boolean available(); @@ -46,20 +46,20 @@ public abstract class Enabled implements ErrorsHolder { public abstract List orphanedRefs(); - Enabled() { + SyncStatus() { } @SerializedNames({ "available", "enabled", "lastSync", "aheadRefs", "divergedRefs", "orphanedRefs", "errors" }) - public static Enabled create(final Boolean available, - final Boolean enabled, - final Long lastSync, - final List aheadRefs, - final List divergedRefs, - final List orphanedRefs, - final List errors) { + public static SyncStatus create(final Boolean available, + final Boolean enabled, + final Long lastSync, + final List aheadRefs, + final List divergedRefs, + final List orphanedRefs, + final List errors) { - return new AutoValue_Enabled(BitbucketUtils.nullToEmpty(errors), + return new AutoValue_SyncStatus(BitbucketUtils.nullToEmpty(errors), available != null ? available : true, enabled != null ? enabled : false, lastSync != null ? lastSync : 0, diff --git a/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java b/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java index 6b687ab9..f5931b97 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java +++ b/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java @@ -63,7 +63,7 @@ import com.cdancy.bitbucket.rest.domain.repository.PullRequestSettings; import com.cdancy.bitbucket.rest.domain.repository.Repository; import com.cdancy.bitbucket.rest.domain.repository.RepositoryPage; -import com.cdancy.bitbucket.rest.domain.sync.Enabled; +import com.cdancy.bitbucket.rest.domain.sync.SyncStatus; import com.cdancy.bitbucket.rest.domain.tags.Tag; import com.cdancy.bitbucket.rest.domain.tags.TagPage; import com.google.common.collect.Lists; @@ -367,7 +367,7 @@ public Object createOrPropagate(final Throwable throwable) throws Exception { } } - public static final class EnabledOnError implements Fallback { + public static final class SyncStatusOnError implements Fallback { @Override public Object createOrPropagate(final Throwable throwable) throws Exception { if (checkNotNull(throwable, "throwable") != null) { @@ -375,9 +375,9 @@ public Object createOrPropagate(final Throwable throwable) throws Exception { final boolean isAvailable = (is204 != null) ? true : false; final List errors = getErrors(throwable.getMessage()); if (errors.size() > 0 && errors.get(0).context().startsWith("Error parsing input: null")) { - return createEnabledFromErrors(isAvailable, null); + return createSyncStatusFromErrors(isAvailable, null); } else { - return createEnabledFromErrors(isAvailable, errors); + return createSyncStatusFromErrors(isAvailable, errors); } } throw propagate(throwable); @@ -619,8 +619,8 @@ public static PullRequest createPullRequestFromErrors(final List errors) null, null, errors); } - public static Enabled createEnabledFromErrors(final boolean isAvailable, final List errors) { - return Enabled.create(isAvailable, false, null, null, null, null, errors); + public static SyncStatus createSyncStatusFromErrors(final boolean isAvailable, final List errors) { + return SyncStatus.create(isAvailable, false, null, null, null, null, errors); } public static ActivitiesPage createActivitiesPageFromErrors(final List errors) { diff --git a/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java b/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java index b184178e..3540fe77 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java +++ b/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java @@ -18,9 +18,10 @@ package com.cdancy.bitbucket.rest.features; import com.cdancy.bitbucket.rest.annotations.Documentation; -import com.cdancy.bitbucket.rest.domain.sync.Enabled; +import com.cdancy.bitbucket.rest.domain.sync.SyncStatus; import com.cdancy.bitbucket.rest.fallbacks.BitbucketFallbacks; import com.cdancy.bitbucket.rest.filters.BitbucketAuthenticationFilter; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.rest.annotations.Fallback; import org.jclouds.rest.annotations.Payload; import org.jclouds.rest.annotations.PayloadParam; @@ -28,10 +29,12 @@ import javax.inject.Named; import javax.ws.rs.Consumes; +import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; @Produces(MediaType.APPLICATION_JSON) @@ -45,9 +48,19 @@ public interface SyncApi { @Consumes(MediaType.APPLICATION_JSON) @Path("/{project}/repos/{repo}") @Payload("%7B \"enabled\": \"{enabled}\" %7D") - @Fallback(BitbucketFallbacks.EnabledOnError.class) + @Fallback(BitbucketFallbacks.SyncStatusOnError.class) @POST - Enabled enable(@PathParam("project") String project, - @PathParam("repo") String repo, - @PayloadParam("enabled") boolean enabled); + SyncStatus enable(@PathParam("project") String project, + @PathParam("repo") String repo, + @PayloadParam("enabled") boolean enabled); + + @Named("sync:status") + @Documentation({"https://docs.atlassian.com/DAC/rest/stash/3.7.2/stash-repository-ref-sync-rest.html"}) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{project}/repos/{repo}") + @Fallback(BitbucketFallbacks.SyncStatusOnError.class) + @GET + SyncStatus status(@PathParam("project") String project, + @PathParam("repo") String repo, + @Nullable @QueryParam("at") String at); } From 52a8134f8ae90607a9c70e0098328dbeb26e4d2b Mon Sep 17 00:00:00 2001 From: ChristopherDancy Date: Sun, 13 Jan 2019 20:14:42 -0500 Subject: [PATCH 4/7] Added mockTest for SyncApi.status --- .../rest/features/SyncApiMockTest.java | 92 +++++++++++++++---- 1 file changed, 73 insertions(+), 19 deletions(-) diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java index a24e63b4..329db66e 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java @@ -20,11 +20,14 @@ import com.cdancy.bitbucket.rest.BaseBitbucketMockTest; import com.cdancy.bitbucket.rest.BitbucketApi; import com.cdancy.bitbucket.rest.BitbucketApiMetadata; -import com.cdancy.bitbucket.rest.domain.sync.Enabled; +import com.cdancy.bitbucket.rest.domain.sync.SyncStatus; +import com.google.common.collect.ImmutableMap; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; import org.testng.annotations.Test; +import java.util.Map; + import static org.assertj.core.api.Assertions.assertThat; @Test(groups = "unit", testName = "SyncApiMockTest") @@ -38,12 +41,11 @@ public class SyncApiMockTest extends BaseBitbucketMockTest { public void testEnabled() throws Exception { final MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setBody(payloadFromResource("/sync-enabled.json")).setResponseCode(200)); - final BitbucketApi baseApi = api(server.getUrl("/")); - final SyncApi api = baseApi.syncApi(); - try { - final Enabled status = api.enable(projectKey, repoKey, true); + + try (final BitbucketApi baseApi = api(server.getUrl("/"));) { + + final SyncStatus status = baseApi.syncApi().enable(projectKey, repoKey, true); assertThat(status.available()).isTrue(); assertThat(status.enabled()).isTrue(); assertThat(status.divergedRefs()).isNotEmpty(); @@ -52,19 +54,17 @@ public void testEnabled() throws Exception { assertSent(server, "POST", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); } finally { - baseApi.close(); server.shutdown(); } } public void testDisabled() throws Exception { final MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setResponseCode(204)); - final BitbucketApi baseApi = api(server.getUrl("/")); - final SyncApi api = baseApi.syncApi(); - try { - final Enabled status = api.enable(projectKey, repoKey, true); + + try (final BitbucketApi baseApi = api(server.getUrl("/"));) { + + final SyncStatus status = baseApi.syncApi().enable(projectKey, repoKey, true); assertThat(status.available()).isTrue(); assertThat(status.enabled()).isFalse(); assertThat(status.divergedRefs()).isEmpty(); @@ -72,19 +72,17 @@ public void testDisabled() throws Exception { assertSent(server, "POST", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); } finally { - baseApi.close(); server.shutdown(); } } public void testEnabledOnError() throws Exception { final MockWebServer server = mockWebServer(); - server.enqueue(new MockResponse().setBody(payloadFromResource("/errors.json")).setResponseCode(400)); - final BitbucketApi baseApi = api(server.getUrl("/")); - final SyncApi api = baseApi.syncApi(); - try { - final Enabled status = api.enable(projectKey, repoKey, true); + + try (final BitbucketApi baseApi = api(server.getUrl("/"));) { + + final SyncStatus status = baseApi.syncApi().enable(projectKey, repoKey, true); assertThat(status.available()).isFalse(); assertThat(status.enabled()).isFalse(); assertThat(status.divergedRefs()).isEmpty(); @@ -92,7 +90,63 @@ public void testEnabledOnError() throws Exception { assertSent(server, "POST", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); } finally { - baseApi.close(); + server.shutdown(); + } + } + + public void testStatus() throws Exception { + final MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/sync-enabled.json")).setResponseCode(200)); + + try (final BitbucketApi baseApi = api(server.getUrl("/"));) { + + final SyncStatus status = baseApi.syncApi().status(projectKey, repoKey, null); + assertThat(status.available()).isTrue(); + assertThat(status.enabled()).isTrue(); + assertThat(status.divergedRefs()).isNotEmpty(); + assertThat(status.divergedRefs().get(0).state()).isEqualTo("DIVERGED"); + assertThat(status.errors()).isEmpty(); + + assertSent(server, "GET", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); + } finally { + server.shutdown(); + } + } + + public void testStatusAt() throws Exception { + final MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/sync-enabled.json")).setResponseCode(200)); + + try (final BitbucketApi baseApi = api(server.getUrl("/"));) { + + final SyncStatus status = baseApi.syncApi().status(projectKey, repoKey, "somereference"); + assertThat(status.available()).isTrue(); + assertThat(status.enabled()).isTrue(); + assertThat(status.divergedRefs()).isNotEmpty(); + assertThat(status.divergedRefs().get(0).state()).isEqualTo("DIVERGED"); + assertThat(status.errors()).isEmpty(); + + final Map queryParams = ImmutableMap.of("at", "somereference"); + assertSent(server, "GET", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath, queryParams); + } finally { + server.shutdown(); + } + } + + public void testStatusOnError() throws Exception { + final MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/errors.json")).setResponseCode(400)); + + try (final BitbucketApi baseApi = api(server.getUrl("/"));) { + + final SyncStatus status = baseApi.syncApi().status(projectKey, repoKey, null); + assertThat(status.available()).isFalse(); + assertThat(status.enabled()).isFalse(); + assertThat(status.divergedRefs()).isEmpty(); + assertThat(status.errors()).isNotEmpty(); + + assertSent(server, "GET", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); + } finally { server.shutdown(); } } From aa2ece127ceeb465dfaec19f60a379aeae9f544f Mon Sep 17 00:00:00 2001 From: ChristopherDancy Date: Mon, 14 Jan 2019 09:57:02 -0500 Subject: [PATCH 5/7] SyncApi gained endpoint 'synchronize' --- config/checkstyle/checkstyle.xml | 2 +- .../rest/domain/common/Reference.java | 50 +++++++++++----- .../rest/fallbacks/BitbucketFallbacks.java | 22 +++++++ .../bitbucket/rest/features/SyncApi.java | 14 +++++ .../bitbucket/rest/options/Context.java | 37 ++++++++++++ .../bitbucket/rest/options/SyncOptions.java | 60 +++++++++++++++++++ .../rest/features/PullRequestApiMockTest.java | 2 +- 7 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/cdancy/bitbucket/rest/options/Context.java create mode 100644 src/main/java/com/cdancy/bitbucket/rest/options/SyncOptions.java diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index a50537bf..892d601e 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -162,7 +162,7 @@ - + diff --git a/src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java b/src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java index d51c08ba..44e52ce2 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java +++ b/src/main/java/com/cdancy/bitbucket/rest/domain/common/Reference.java @@ -17,14 +17,17 @@ package com.cdancy.bitbucket.rest.domain.common; +import com.cdancy.bitbucket.rest.BitbucketUtils; import com.cdancy.bitbucket.rest.domain.pullrequest.MinimalRepository; import org.jclouds.javax.annotation.Nullable; import org.jclouds.json.SerializedNames; import com.google.auto.value.AutoValue; +import java.util.List; + @AutoValue -public abstract class Reference { +public abstract class Reference implements ErrorsHolder { // default to 'refs/heads/master' if null @Nullable @@ -63,22 +66,41 @@ public static Reference create(final String id, final MinimalRepository repository, final String displayId) { - return create(id, repository, null, null, displayId, null); + return create(id, repository, null, null, displayId, null, null); + } + + @Deprecated + public static Reference create(final String id, + final MinimalRepository repository, + final String state, + final Boolean tag, + final String displayId, + final String latestCommit) { + + return create(id, + repository, + state, + tag, + displayId, + latestCommit, + null); } - @SerializedNames({"id", "repository", "state", "tag", "displayId", "latestCommit"}) + @SerializedNames({ "id", "repository", "state", "tag", "displayId", "latestCommit", "errors" }) public static Reference create(final String id, - final MinimalRepository repository, - final String state, - final Boolean tag, - final String displayId, - final String latestCommit) { + final MinimalRepository repository, + final String state, + final Boolean tag, + final String displayId, + final String latestCommit, + final List errors) { - return new AutoValue_Reference(id != null ? id : "refs/heads/master", - repository, - state, - tag, - displayId, - latestCommit); + return new AutoValue_Reference(BitbucketUtils.nullToEmpty(errors), + id != null ? id : "refs/heads/master", + repository, + state, + tag, + displayId, + latestCommit); } } diff --git a/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java b/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java index f5931b97..463c9de0 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java +++ b/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java @@ -37,6 +37,7 @@ import com.cdancy.bitbucket.rest.domain.commit.Commit; import com.cdancy.bitbucket.rest.domain.commit.CommitPage; import com.cdancy.bitbucket.rest.domain.common.Error; +import com.cdancy.bitbucket.rest.domain.common.Reference; import com.cdancy.bitbucket.rest.domain.common.RequestStatus; import com.cdancy.bitbucket.rest.domain.common.Veto; import com.cdancy.bitbucket.rest.domain.defaultreviewers.Condition; @@ -384,6 +385,23 @@ public Object createOrPropagate(final Throwable throwable) throws Exception { } } + public static final class ReferenceOnError implements Fallback { + @Override + public Object createOrPropagate(final Throwable throwable) throws Exception { + if (checkNotNull(throwable, "throwable") != null) { + final Boolean is204 = returnValueOnCodeOrNull(throwable, true, equalTo(204)); + final String syncedStatus = (is204 != null) ? "SYNCED" : null; + final List errors = getErrors(throwable.getMessage()); + if (errors.size() > 0 && errors.get(0).context().startsWith("Error parsing input: null")) { + return createReferenceFromErrors(syncedStatus, null); + } else { + return createReferenceFromErrors(syncedStatus, errors); + } + } + throw propagate(throwable); + } + } + public static final class ActivitiesPageOnError implements Fallback { @Override public Object createOrPropagate(final Throwable throwable) throws Exception { @@ -619,6 +637,10 @@ public static PullRequest createPullRequestFromErrors(final List errors) null, null, errors); } + public static Reference createReferenceFromErrors(final String syncedStatus, final List errors) { + return Reference.create(null, null, syncedStatus, null, null, null, errors); + } + public static SyncStatus createSyncStatusFromErrors(final boolean isAvailable, final List errors) { return SyncStatus.create(isAvailable, false, null, null, null, null, errors); } diff --git a/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java b/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java index 3540fe77..4e2c9d54 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java +++ b/src/main/java/com/cdancy/bitbucket/rest/features/SyncApi.java @@ -18,14 +18,18 @@ package com.cdancy.bitbucket.rest.features; import com.cdancy.bitbucket.rest.annotations.Documentation; +import com.cdancy.bitbucket.rest.domain.common.Reference; import com.cdancy.bitbucket.rest.domain.sync.SyncStatus; import com.cdancy.bitbucket.rest.fallbacks.BitbucketFallbacks; import com.cdancy.bitbucket.rest.filters.BitbucketAuthenticationFilter; +import com.cdancy.bitbucket.rest.options.SyncOptions; import org.jclouds.javax.annotation.Nullable; +import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.Fallback; import org.jclouds.rest.annotations.Payload; import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.binders.BindToJsonPayload; import javax.inject.Named; import javax.ws.rs.Consumes; @@ -63,4 +67,14 @@ SyncStatus enable(@PathParam("project") String project, SyncStatus status(@PathParam("project") String project, @PathParam("repo") String repo, @Nullable @QueryParam("at") String at); + + @Named("sync:synchronize") + @Documentation({"https://docs.atlassian.com/DAC/rest/stash/3.7.2/stash-repository-ref-sync-rest.html"}) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{project}/repos/{repo}/synchronize") + @Fallback(BitbucketFallbacks.ReferenceOnError.class) + @POST + Reference synchronize(@PathParam("project") String project, + @PathParam("repo") String repo, + @BinderParam(BindToJsonPayload.class) SyncOptions syncOptions); } diff --git a/src/main/java/com/cdancy/bitbucket/rest/options/Context.java b/src/main/java/com/cdancy/bitbucket/rest/options/Context.java new file mode 100644 index 00000000..fd3fd31c --- /dev/null +++ b/src/main/java/com/cdancy/bitbucket/rest/options/Context.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cdancy.bitbucket.rest.options; + +import com.google.auto.value.AutoValue; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +@AutoValue +public abstract class Context { + + @Nullable + public abstract String commitMessage(); + + Context() { + } + + @SerializedNames({ "commitMessage" }) + public static Context create(final String commitMessage) { + return new AutoValue_Context(commitMessage); + } +} diff --git a/src/main/java/com/cdancy/bitbucket/rest/options/SyncOptions.java b/src/main/java/com/cdancy/bitbucket/rest/options/SyncOptions.java new file mode 100644 index 00000000..b2f60def --- /dev/null +++ b/src/main/java/com/cdancy/bitbucket/rest/options/SyncOptions.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cdancy.bitbucket.rest.options; + +import com.google.auto.value.AutoValue; +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +@AutoValue +public abstract class SyncOptions { + + public enum ACTION { + MERGE, + DISCARD + } + + public abstract String refId(); + + public abstract ACTION action(); + + @Nullable + public abstract Context context(); + + SyncOptions() { + } + + public static SyncOptions create(final String refId, + final String action, + final String message) { + return create(refId, ACTION.valueOf(action), Context.create(message)); + } + + public static SyncOptions create(final String refId, + final ACTION action, + final String message) { + return create(refId, action, Context.create(message)); + } + + @SerializedNames({ "refId", "action", "context" }) + public static SyncOptions create(final String refId, + final ACTION action, + final Context context) { + return new AutoValue_SyncOptions(refId, action, context); + } +} diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiMockTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiMockTest.java index 26d0e8a3..244cf7d2 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiMockTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/PullRequestApiMockTest.java @@ -90,7 +90,7 @@ public void testCreatePullRequest() throws Exception { final MinimalRepository repository2 = MinimalRepository.create(repoKey, null, proj2); final String commitId = "930228bb501e07c2653771858320873d94518e33"; - final Reference fromRef = Reference.create("refs/heads/feature-ABC-123", repository1, null, null, "feature-ABC-123", commitId); + final Reference fromRef = Reference.create("refs/heads/feature-ABC-123", repository1, null, null, "feature-ABC-123", commitId, null); final Reference toRef = Reference.create("refs/heads/master", repository2); final CreatePullRequest cpr = CreatePullRequest.create("Talking Nerdy", "Some description", fromRef, toRef, null, null); final PullRequest pr = api.create(repository2.project().key(), repository2.slug(), cpr); From 78d4d27dbcea64213a8aa24bc4866e043c30cc3b Mon Sep 17 00:00:00 2001 From: ChristopherDancy Date: Mon, 14 Jan 2019 10:19:19 -0500 Subject: [PATCH 6/7] Added mockTests for SyncApi.synchronize --- .../bitbucket/rest/BaseBitbucketMockTest.java | 2 + .../rest/features/SyncApiMockTest.java | 64 +++++++++++++++++-- src/test/resources/synchronize.json | 6 ++ 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/test/resources/synchronize.json diff --git a/src/test/java/com/cdancy/bitbucket/rest/BaseBitbucketMockTest.java b/src/test/java/com/cdancy/bitbucket/rest/BaseBitbucketMockTest.java index 1daefd27..69d48d92 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/BaseBitbucketMockTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/BaseBitbucketMockTest.java @@ -49,6 +49,8 @@ */ public class BaseBitbucketMockTest { + protected final String postMethod = "POST"; + protected final String restBasePath = "/rest/api/"; protected final String provider; private final JsonParser parser = new JsonParser(); diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java index 329db66e..35a0e56e 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java @@ -20,7 +20,9 @@ import com.cdancy.bitbucket.rest.BaseBitbucketMockTest; import com.cdancy.bitbucket.rest.BitbucketApi; import com.cdancy.bitbucket.rest.BitbucketApiMetadata; +import com.cdancy.bitbucket.rest.domain.common.Reference; import com.cdancy.bitbucket.rest.domain.sync.SyncStatus; +import com.cdancy.bitbucket.rest.options.SyncOptions; import com.google.common.collect.ImmutableMap; import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.MockWebServer; @@ -34,10 +36,10 @@ public class SyncApiMockTest extends BaseBitbucketMockTest { private final String restApiPath = "/rest/sync/"; - private final String projectKey = "PRJ"; private final String repoKey = "my-repo"; private final String syncPath = "/projects/" + projectKey + "/repos/" + repoKey; + private final String refsHeadsMaster = "refs/heads/master"; public void testEnabled() throws Exception { final MockWebServer server = mockWebServer(); @@ -52,7 +54,7 @@ public void testEnabled() throws Exception { assertThat(status.divergedRefs().get(0).state()).isEqualTo("DIVERGED"); assertThat(status.errors()).isEmpty(); - assertSent(server, "POST", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); + assertSent(server, postMethod, restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); } finally { server.shutdown(); } @@ -70,7 +72,7 @@ public void testDisabled() throws Exception { assertThat(status.divergedRefs()).isEmpty(); assertThat(status.errors()).isEmpty(); - assertSent(server, "POST", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); + assertSent(server, postMethod, restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); } finally { server.shutdown(); } @@ -88,7 +90,7 @@ public void testEnabledOnError() throws Exception { assertThat(status.divergedRefs()).isEmpty(); assertThat(status.errors()).isNotEmpty(); - assertSent(server, "POST", restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); + assertSent(server, postMethod, restApiPath + BitbucketApiMetadata.API_VERSION + syncPath); } finally { server.shutdown(); } @@ -150,4 +152,58 @@ public void testStatusOnError() throws Exception { server.shutdown(); } } + + public void testSynchronize() throws Exception { + final MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/synchronize.json")).setResponseCode(200)); + + try (final BitbucketApi baseApi = api(server.getUrl("/"));) { + + final SyncOptions options = SyncOptions.create(refsHeadsMaster, SyncOptions.ACTION.MERGE, "hello world"); + final Reference ref = baseApi.syncApi().synchronize(projectKey, repoKey, options); + assertThat(ref.id()).isEqualTo(refsHeadsMaster); + assertThat(ref.state()).isEqualTo("AHEAD"); + assertThat(ref.errors()).isEmpty(); + + assertSent(server, postMethod, restApiPath + BitbucketApiMetadata.API_VERSION + syncPath + "/synchronize"); + } finally { + server.shutdown(); + } + } + + public void testSynchronizeInSync() throws Exception { + final MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/synchronize.json")).setResponseCode(204)); + + try (final BitbucketApi baseApi = api(server.getUrl("/"));) { + + final SyncOptions options = SyncOptions.create(refsHeadsMaster, SyncOptions.ACTION.MERGE, "merge message"); + final Reference ref = baseApi.syncApi().synchronize(projectKey, repoKey, options); + assertThat(ref.id()).isEqualTo(refsHeadsMaster); + assertThat(ref.state()).isEqualTo("SYNCED"); + assertThat(ref.errors()).isEmpty(); + + assertSent(server, postMethod, restApiPath + BitbucketApiMetadata.API_VERSION + syncPath + "/synchronize"); + } finally { + server.shutdown(); + } + } + + public void testSynchronizeOnError() throws Exception { + final MockWebServer server = mockWebServer(); + server.enqueue(new MockResponse().setBody(payloadFromResource("/errors.json")).setResponseCode(400)); + + try (final BitbucketApi baseApi = api(server.getUrl("/"));) { + + final SyncOptions options = SyncOptions.create(refsHeadsMaster, SyncOptions.ACTION.MERGE, "hello world"); + final Reference ref = baseApi.syncApi().synchronize(projectKey, repoKey, options); + assertThat(ref.id()).isEqualTo(refsHeadsMaster); + assertThat(ref.state()).isNull(); + assertThat(ref.errors()).isNotEmpty(); + + assertSent(server, postMethod, restApiPath + BitbucketApiMetadata.API_VERSION + syncPath + "/synchronize"); + } finally { + server.shutdown(); + } + } } diff --git a/src/test/resources/synchronize.json b/src/test/resources/synchronize.json new file mode 100644 index 00000000..e72aeae1 --- /dev/null +++ b/src/test/resources/synchronize.json @@ -0,0 +1,6 @@ +{ + "id": "refs/heads/master", + "displayId": "master", + "state": "AHEAD", + "tag": false +} \ No newline at end of file From 667ead64f0b1c7bc86a266015abbbb66f1c0dd99 Mon Sep 17 00:00:00 2001 From: ChristopherDancy Date: Sat, 16 Feb 2019 13:41:04 -0500 Subject: [PATCH 7/7] General refactoring in support of SyncApi. --- .../rest/domain/common/RequestStatus.java | 2 +- .../rest/domain/repository/Repository.java | 11 +- .../rest/fallbacks/BitbucketFallbacks.java | 11 +- .../rest/features/RepositoryApi.java | 14 ++ .../bitbucket/rest/options/SyncOptions.java | 22 ++- .../bitbucket/rest/GeneratedTestContents.java | 22 ++- .../cdancy/bitbucket/rest/TestUtilities.java | 32 +++-- .../rest/features/BranchApiLiveTest.java | 2 +- .../rest/features/RepositoryApiLiveTest.java | 23 +++- .../rest/features/RepositoryApiMockTest.java | 52 +++++++ .../rest/features/SyncApiLiveTest.java | 128 ++++++++++++++++++ .../rest/features/SyncApiMockTest.java | 6 +- src/test/resources/repository-fork.json | 81 +++++++++++ 13 files changed, 366 insertions(+), 40 deletions(-) create mode 100644 src/test/java/com/cdancy/bitbucket/rest/features/SyncApiLiveTest.java create mode 100644 src/test/resources/repository-fork.json diff --git a/src/main/java/com/cdancy/bitbucket/rest/domain/common/RequestStatus.java b/src/main/java/com/cdancy/bitbucket/rest/domain/common/RequestStatus.java index 26368592..a2754302 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/domain/common/RequestStatus.java +++ b/src/main/java/com/cdancy/bitbucket/rest/domain/common/RequestStatus.java @@ -40,7 +40,7 @@ public abstract class RequestStatus implements Value, ErrorsHolder { public static RequestStatus create(@Nullable final Boolean value, final List errors) { - return new AutoValue_RequestStatus(value, + return new AutoValue_RequestStatus(value, BitbucketUtils.nullToEmpty(errors)); } } diff --git a/src/main/java/com/cdancy/bitbucket/rest/domain/repository/Repository.java b/src/main/java/com/cdancy/bitbucket/rest/domain/repository/Repository.java index 5f5f9294..8395a5d6 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/domain/repository/Repository.java +++ b/src/main/java/com/cdancy/bitbucket/rest/domain/repository/Repository.java @@ -52,6 +52,9 @@ public abstract class Repository implements ErrorsHolder, LinksHolder { public abstract boolean forkable(); + @Nullable + public abstract Repository origin(); + @Nullable public abstract Project project(); @@ -61,8 +64,8 @@ public abstract class Repository implements ErrorsHolder, LinksHolder { } @SerializedNames({ "slug", "id", "name", "scmId", - "state", "statusMessage", "forkable", "project", - "public", "links", "errors" }) + "state", "statusMessage", "forkable", "origin", + "project", "public", "links", "errors" }) public static Repository create(final String slug, final int id, final String name, @@ -70,6 +73,7 @@ public static Repository create(final String slug, final String state, final String statusMessage, final boolean forkable, + final Repository origin, final Project project, final boolean _public, final Links links, @@ -83,7 +87,8 @@ public static Repository create(final String slug, scmId, state, statusMessage, - forkable, + forkable, + origin, project, _public); } diff --git a/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java b/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java index 463c9de0..0e2ef30a 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java +++ b/src/main/java/com/cdancy/bitbucket/rest/fallbacks/BitbucketFallbacks.java @@ -375,7 +375,9 @@ public Object createOrPropagate(final Throwable throwable) throws Exception { final Boolean is204 = returnValueOnCodeOrNull(throwable, true, equalTo(204)); final boolean isAvailable = (is204 != null) ? true : false; final List errors = getErrors(throwable.getMessage()); - if (errors.size() > 0 && errors.get(0).context().startsWith("Error parsing input: null")) { + if (errors.size() > 0 + && errors.get(0).context() != null + && errors.get(0).context().startsWith("Error parsing input: null")) { return createSyncStatusFromErrors(isAvailable, null); } else { return createSyncStatusFromErrors(isAvailable, errors); @@ -392,7 +394,10 @@ public Object createOrPropagate(final Throwable throwable) throws Exception { final Boolean is204 = returnValueOnCodeOrNull(throwable, true, equalTo(204)); final String syncedStatus = (is204 != null) ? "SYNCED" : null; final List errors = getErrors(throwable.getMessage()); - if (errors.size() > 0 && errors.get(0).context().startsWith("Error parsing input: null")) { + if (errors.size() > 0 + && errors.get(0).context() != null + && errors.get(0).context().startsWith("Error parsing input: null")) { + return createReferenceFromErrors(syncedStatus, null); } else { return createReferenceFromErrors(syncedStatus, errors); @@ -591,7 +596,7 @@ public static Task createTaskFromErrors(final List errors) { } public static Repository createRepositoryFromErrors(final List errors) { - return Repository.create(null, -1, null, null, null, null, false, null, false, null, errors); + return Repository.create(null, -1, null, null, null, null, false, null, null, false, null, errors); } public static RepositoryPage createRepositoryPageFromErrors(final List errors) { diff --git a/src/main/java/com/cdancy/bitbucket/rest/features/RepositoryApi.java b/src/main/java/com/cdancy/bitbucket/rest/features/RepositoryApi.java index d34984e4..2a4c0110 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/features/RepositoryApi.java +++ b/src/main/java/com/cdancy/bitbucket/rest/features/RepositoryApi.java @@ -33,6 +33,8 @@ import org.jclouds.javax.annotation.Nullable; import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.Payload; +import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.binders.BindToJsonPayload; import javax.ws.rs.Consumes; @@ -71,6 +73,18 @@ Repository create(@PathParam("project") String project, Repository get(@PathParam("project") String project, @PathParam("repo") String repo); + @Named("repository:fork") + @Documentation({"https://developer.atlassian.com/static/rest/bitbucket-server/latest/bitbucket-rest.html#idm45888277587248"}) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{project}/repos/{repo}") + @Payload("%7B \"name\": \"{newRepo}\", \"project\": %7B \"key\": \"{newProject}\" %7D %7D") + @Fallback(BitbucketFallbacks.RepositoryOnError.class) + @POST + Repository fork(@PathParam("project") String project, + @PathParam("repo") String repo, + @PayloadParam("newProject") String newProject, + @PayloadParam("newRepo") String newRepo); + @Named("repository:delete") @Documentation({"https://developer.atlassian.com/static/rest/bitbucket-server/latest/bitbucket-rest.html#idm45888277567792"}) @Consumes(MediaType.APPLICATION_JSON) diff --git a/src/main/java/com/cdancy/bitbucket/rest/options/SyncOptions.java b/src/main/java/com/cdancy/bitbucket/rest/options/SyncOptions.java index b2f60def..d8f23c57 100644 --- a/src/main/java/com/cdancy/bitbucket/rest/options/SyncOptions.java +++ b/src/main/java/com/cdancy/bitbucket/rest/options/SyncOptions.java @@ -39,22 +39,20 @@ public enum ACTION { SyncOptions() { } - public static SyncOptions create(final String refId, - final String action, - final String message) { - return create(refId, ACTION.valueOf(action), Context.create(message)); + public static SyncOptions merge(@Nullable final String refId) { + return create(refId, ACTION.MERGE, null); } - public static SyncOptions create(final String refId, - final ACTION action, - final String message) { - return create(refId, action, Context.create(message)); + public static SyncOptions discard(@Nullable final String refId) { + return create(refId, ACTION.DISCARD, null); } @SerializedNames({ "refId", "action", "context" }) - public static SyncOptions create(final String refId, - final ACTION action, - final Context context) { - return new AutoValue_SyncOptions(refId, action, context); + public static SyncOptions create(@Nullable final String refId, + @Nullable final ACTION action, + @Nullable final Context context) { + return new AutoValue_SyncOptions(refId != null ? refId : "refs/heads/master", + action != null ? action : ACTION.MERGE, + context); } } diff --git a/src/test/java/com/cdancy/bitbucket/rest/GeneratedTestContents.java b/src/test/java/com/cdancy/bitbucket/rest/GeneratedTestContents.java index d70b3c8f..2f198611 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/GeneratedTestContents.java +++ b/src/test/java/com/cdancy/bitbucket/rest/GeneratedTestContents.java @@ -19,16 +19,20 @@ import com.cdancy.bitbucket.rest.domain.project.Project; import com.cdancy.bitbucket.rest.domain.repository.Repository; +import com.google.common.collect.Lists; + +import java.util.List; /** * Umbrella for all generated test contents. */ public class GeneratedTestContents { - + public final Project project; public final Repository repository; - public final Repository emptyRepository; - + public final String emptyRepositoryName; + public final List projectRepoMapping = Lists.newArrayList(); + public final boolean projectPreviouslyExists; /** @@ -36,16 +40,22 @@ public class GeneratedTestContents { * * @param project previously created Project. * @param repository previously created Repository. + * @param emptyRepositoryName previously created Repository with no contents. * @param projectPreviouslyExists whether the test suite created or user passed in. */ public GeneratedTestContents(final Project project, - final Repository repository, - final Repository emptyRepository, + final Repository repository, + final String emptyRepositoryName, final boolean projectPreviouslyExists) { this.project = project; this.repository = repository; - this.emptyRepository = emptyRepository; + this.emptyRepositoryName = emptyRepositoryName; this.projectPreviouslyExists = projectPreviouslyExists; } + + public void addRepoForDeletion(final String project, final String repository) { + final String[] mapping = {project , repository}; + projectRepoMapping.add(mapping); + } } diff --git a/src/test/java/com/cdancy/bitbucket/rest/TestUtilities.java b/src/test/java/com/cdancy/bitbucket/rest/TestUtilities.java index 11c69009..fee37328 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/TestUtilities.java +++ b/src/test/java/com/cdancy/bitbucket/rest/TestUtilities.java @@ -205,7 +205,10 @@ public static synchronized GeneratedTestContents initGeneratedTestContents(final throw Throwables.propagate(e); } - return new GeneratedTestContents(project, repository, emptyRepository, projectPreviouslyExists); + final GeneratedTestContents gtc = new GeneratedTestContents(project, repository, emptyRepository.name(), projectPreviouslyExists); + gtc.addRepoForDeletion(projectKey, emptyRepoKey); + gtc.addRepoForDeletion(projectKey, repoKey); + return gtc; } /** @@ -283,20 +286,31 @@ public static synchronized void terminateGeneratedTestContents(final BitbucketAp final Project project = generatedTestContents.project; final Repository repository = generatedTestContents.repository; - final Repository emptyRepository = generatedTestContents.emptyRepository; - // delete the empty repository - final RequestStatus emptyRepoSuccess = api.repositoryApi().delete(project.key(), emptyRepository.name()); - assertThat(emptyRepoSuccess).isNotNull(); - assertThat(emptyRepoSuccess.value()).isTrue(); - assertThat(emptyRepoSuccess.errors()).isEmpty(); - - // delete repository + // delete main repository final RequestStatus success = api.repositoryApi().delete(project.key(), repository.name()); assertThat(success).isNotNull(); assertThat(success.value()).isTrue(); assertThat(success.errors()).isEmpty(); + // delete all attached repos + for (final String[] mapping : generatedTestContents.projectRepoMapping) { + + final String projectKey = mapping[0]; + final String repoKey = mapping[1]; + + final RequestStatus successInner = api.repositoryApi().delete(projectKey, repoKey); + assertThat(successInner).isNotNull(); + if (!successInner.errors().isEmpty()) { + if (!successInner.errors().get(0).context().contains("does not exist")) { + throw new RuntimeException("Failed deleting repo: " + successInner.errors().get(0).context()); + } + } else { + assertThat(successInner.value()).isTrue(); + assertThat(successInner.errors()).isEmpty(); + } + } + // delete project if (!generatedTestContents.projectPreviouslyExists) { final RequestStatus deleteStatus = api.projectApi().delete(project.key()); diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/BranchApiLiveTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/BranchApiLiveTest.java index 2a664990..a2b9cd24 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/BranchApiLiveTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/BranchApiLiveTest.java @@ -64,7 +64,7 @@ public void init() { generatedTestContents = TestUtilities.initGeneratedTestContents(this.endpoint, this.bitbucketAuthentication, this.api); this.projectKey = generatedTestContents.project.key(); this.repoKey = generatedTestContents.repository.name(); - this.emptyRepoKey = generatedTestContents.emptyRepository.name(); + this.emptyRepoKey = generatedTestContents.emptyRepositoryName; final Branch branch = api().getDefault(projectKey, repoKey); assertThat(branch).isNotNull(); diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/RepositoryApiLiveTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/RepositoryApiLiveTest.java index 311a6013..316486a2 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/RepositoryApiLiveTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/RepositoryApiLiveTest.java @@ -17,6 +17,8 @@ package com.cdancy.bitbucket.rest.features; +import static com.cdancy.bitbucket.rest.TestUtilities.randomStringLettersOnly; + import com.cdancy.bitbucket.rest.BaseBitbucketApiLiveTest; import com.cdancy.bitbucket.rest.GeneratedTestContents; import com.cdancy.bitbucket.rest.TestUtilities; @@ -67,6 +69,23 @@ public void testGetRepository() { assertThat(repoKey.equalsIgnoreCase(repository.name())).isTrue(); } + @Test(dependsOnMethods = {testGetRepoKeyword}) + public void testForkRepository() { + final String forkedRepoName = randomStringLettersOnly(); + final Repository repository = api().fork(projectKey, repoKey, projectKey, forkedRepoName); + assertThat(repository).isNotNull(); + assertThat(repository.errors()).isEmpty(); + assertThat(forkedRepoName.equalsIgnoreCase(repository.name())).isTrue(); + generatedTestContents.addRepoForDeletion(projectKey, forkedRepoName); + } + + @Test + public void testForkRepositoryNonExistent() { + final Repository repository = api().fork(projectKey, randomStringLettersOnly(), projectKey, randomStringLettersOnly()); + assertThat(repository).isNotNull(); + assertThat(repository.errors()).isNotEmpty(); + } + @Test(dependsOnMethods = {testGetRepoKeyword}) public void testListRepositories() { final RepositoryPage repositoryPage = api().list(projectKey, 0, 100); @@ -88,7 +107,7 @@ public void testListRepositories() { @Test public void testDeleteRepositoryNonExistent() { - final String random = TestUtilities.randomStringLettersOnly(); + final String random = randomStringLettersOnly(); final RequestStatus success = api().delete(projectKey, random); assertThat(success).isNotNull(); assertThat(success.value()).isFalse(); @@ -97,7 +116,7 @@ public void testDeleteRepositoryNonExistent() { @Test public void testGetRepositoryNonExistent() { - final Repository repository = api().get(projectKey, TestUtilities.randomStringLettersOnly()); + final Repository repository = api().get(projectKey, randomStringLettersOnly()); assertThat(repository).isNotNull(); assertThat(repository.errors().isEmpty()).isFalse(); } diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/RepositoryApiMockTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/RepositoryApiMockTest.java index 1594aef9..938ba750 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/RepositoryApiMockTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/RepositoryApiMockTest.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -155,6 +156,57 @@ public void testGetRepositoryNonExistent() throws Exception { } } + public void testForkRepository() throws Exception { + final MockWebServer server = mockWebServer(); + + server.enqueue(new MockResponse().setBody(payloadFromResource("/repository-fork.json")).setResponseCode(201)); + final BitbucketApi baseApi = api(server.getUrl("/")); + final RepositoryApi api = baseApi.repositoryApi(); + try { + + final String forkName = "hello-world"; + final Repository repository = api.fork(projectKey, repoKey, projectKey, forkName); + assertThat(repository).isNotNull(); + assertThat(repository.errors()).isEmpty(); + assertThat(repository.slug()).isEqualToIgnoringCase(forkName); + assertThat(repository.origin()).isNotNull(); + assertSent(server, postMethod, restApiPath + + BitbucketApiMetadata.API_VERSION + + projectsPath + + projectKey + + reposEndpoint + + "/" + repoKey); + } finally { + baseApi.close(); + server.shutdown(); + } + } + + public void testForkRepositoryWithErrors() throws Exception { + final MockWebServer server = mockWebServer(); + + server.enqueue(new MockResponse().setBody(payloadFromResource("/errors.json")).setResponseCode(400)); + final BitbucketApi baseApi = api(server.getUrl("/")); + final RepositoryApi api = baseApi.repositoryApi(); + try { + + final String forkName = "hello-world"; + final String nonExistentRepo = UUID.randomUUID().toString(); + final Repository repository = api.fork(projectKey, repoKey, nonExistentRepo, forkName); + assertThat(repository).isNotNull(); + assertThat(repository.errors()).isNotEmpty(); + assertSent(server, postMethod, restApiPath + + BitbucketApiMetadata.API_VERSION + + projectsPath + + projectKey + + reposEndpoint + + "/" + repoKey); + } finally { + baseApi.close(); + server.shutdown(); + } + } + public void testDeleteRepository() throws Exception { final MockWebServer server = mockWebServer(); diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiLiveTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiLiveTest.java new file mode 100644 index 00000000..e17495b3 --- /dev/null +++ b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiLiveTest.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cdancy.bitbucket.rest.features; + +import static com.cdancy.bitbucket.rest.TestUtilities.randomStringLettersOnly; + +import com.cdancy.bitbucket.rest.BaseBitbucketApiLiveTest; +import com.cdancy.bitbucket.rest.GeneratedTestContents; +import com.cdancy.bitbucket.rest.TestUtilities; +import com.cdancy.bitbucket.rest.domain.common.Reference; +import com.cdancy.bitbucket.rest.domain.repository.Repository; +import com.cdancy.bitbucket.rest.domain.sync.SyncStatus; +import com.cdancy.bitbucket.rest.options.SyncOptions; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@Test(groups = "live", testName = "SyncApiLiveTest", singleThreaded = true) +public class SyncApiLiveTest extends BaseBitbucketApiLiveTest { + + private GeneratedTestContents generatedTestContents; + + private String projectKey; + private String repoKey; + private String newProjectKey; + private String newRepoKey = randomStringLettersOnly(); + + @BeforeClass + public void init() { + generatedTestContents = TestUtilities.initGeneratedTestContents(this.endpoint, this.bitbucketAuthentication, this.api); + this.projectKey = generatedTestContents.project.key(); + this.repoKey = generatedTestContents.repository.name(); + this.newProjectKey = this.projectKey; + + final Repository repo = api.repositoryApi().fork(projectKey, repoKey, newProjectKey, newRepoKey); + assertThat(repo).isNotNull(); + assertThat(repo.errors()).isEmpty(); + generatedTestContents.addRepoForDeletion(newProjectKey, newRepoKey); + } + + @Test + public void testDisableSync() { + final SyncStatus status = api().enable(newProjectKey, newRepoKey, false); + assertThat(status).isNotNull(); + assertThat(status.available()).isTrue(); + assertThat(status.enabled()).isFalse(); + assertThat(status.divergedRefs()).isEmpty(); + assertThat(status.errors()).isEmpty(); + } + + @Test (dependsOnMethods = "testDisableSync") + public void testEnableSync() { + final SyncStatus status = api().enable(newProjectKey, newRepoKey, true); + assertThat(status).isNotNull(); + assertThat(status.available()).isTrue(); + assertThat(status.enabled()).isTrue(); + assertThat(status.errors()).isEmpty(); + } + + @Test (dependsOnMethods = "testEnableSync") + public void testSynchronzie() { + final Reference status = api().synchronize(newProjectKey, newRepoKey, SyncOptions.discard(null)); + assertThat(status).isNotNull(); + assertThat(status.errors()).isNotEmpty(); + + // expected as the there is no code in the repo + assertThat(status.errors().get(0).message().contains("cannot be synchronized")); + } + + @Test (dependsOnMethods = "testSynchronzie") + public void testGetSyncStatus() { + final SyncStatus status = api().status(newProjectKey, newRepoKey, null); + assertThat(status).isNotNull(); + assertThat(status.available()).isTrue(); + assertThat(status.enabled()).isTrue(); + assertThat(status.aheadRefs()).isEmpty(); + assertThat(status.divergedRefs()).isEmpty(); + assertThat(status.orphanedRefs()).isEmpty(); + assertThat(status.errors()).isEmpty(); + } + + @Test + public void testEnableSyncOnError() { + final SyncStatus status = api().enable(newProjectKey, randomStringLettersOnly(), true); + assertThat(status).isNotNull(); + assertThat(status.errors()).isNotEmpty(); + } + + @Test + public void testSyncStatusOnError() { + final SyncStatus status = api().status(newProjectKey, randomStringLettersOnly(), null); + assertThat(status).isNotNull(); + assertThat(status.errors()).isNotEmpty(); + } + + @Test + public void testSynchronizeOnError() { + final Reference status = api().synchronize(newProjectKey, randomStringLettersOnly(), SyncOptions.merge(null)); + assertThat(status).isNotNull(); + assertThat(status.errors()).isNotEmpty(); + } + + @AfterClass + public void fin() { + TestUtilities.terminateGeneratedTestContents(this.api, generatedTestContents); + } + + private SyncApi api() { + return api.syncApi(); + } +} diff --git a/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java index 35a0e56e..2b45cb28 100644 --- a/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java +++ b/src/test/java/com/cdancy/bitbucket/rest/features/SyncApiMockTest.java @@ -159,7 +159,7 @@ public void testSynchronize() throws Exception { try (final BitbucketApi baseApi = api(server.getUrl("/"));) { - final SyncOptions options = SyncOptions.create(refsHeadsMaster, SyncOptions.ACTION.MERGE, "hello world"); + final SyncOptions options = SyncOptions.merge(refsHeadsMaster); final Reference ref = baseApi.syncApi().synchronize(projectKey, repoKey, options); assertThat(ref.id()).isEqualTo(refsHeadsMaster); assertThat(ref.state()).isEqualTo("AHEAD"); @@ -177,7 +177,7 @@ public void testSynchronizeInSync() throws Exception { try (final BitbucketApi baseApi = api(server.getUrl("/"));) { - final SyncOptions options = SyncOptions.create(refsHeadsMaster, SyncOptions.ACTION.MERGE, "merge message"); + final SyncOptions options = SyncOptions.merge(refsHeadsMaster); final Reference ref = baseApi.syncApi().synchronize(projectKey, repoKey, options); assertThat(ref.id()).isEqualTo(refsHeadsMaster); assertThat(ref.state()).isEqualTo("SYNCED"); @@ -195,7 +195,7 @@ public void testSynchronizeOnError() throws Exception { try (final BitbucketApi baseApi = api(server.getUrl("/"));) { - final SyncOptions options = SyncOptions.create(refsHeadsMaster, SyncOptions.ACTION.MERGE, "hello world"); + final SyncOptions options = SyncOptions.merge(refsHeadsMaster); final Reference ref = baseApi.syncApi().synchronize(projectKey, repoKey, options); assertThat(ref.id()).isEqualTo(refsHeadsMaster); assertThat(ref.state()).isNull(); diff --git a/src/test/resources/repository-fork.json b/src/test/resources/repository-fork.json new file mode 100644 index 00000000..0a0b6538 --- /dev/null +++ b/src/test/resources/repository-fork.json @@ -0,0 +1,81 @@ +{ + "slug": "hello-world", + "id": 2, + "name": "hello world", + "scmId": "git", + "state": "AVAILABLE", + "statusMessage": "Available", + "forkable": true, + "origin": { + "slug": "my-repo", + "id": 1, + "name": "My repo", + "scmId": "git", + "state": "AVAILABLE", + "statusMessage": "Available", + "forkable": true, + "project": { + "key": "PRJ", + "id": 1, + "name": "My Cool Project", + "description": "The description for my cool project.", + "public": true, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://link/to/project" + } + ] + } + }, + "public": true, + "links": { + "clone": [ + { + "href": "ssh://git@/PRJ/my-repo.git", + "name": "ssh" + }, + { + "href": "https:///scm/PRJ/my-repo.git", + "name": "http" + } + ], + "self": [ + { + "href": "http://link/to/repository" + } + ] + } + }, + "project": { + "key": "~JDOE", + "id": 2, + "name": "John Doe", + "type": "PERSONAL", + "links": { + "self": [ + { + "href": "http://link/to/project" + } + ] + } + }, + "links": { + "clone": [ + { + "href": "https:///scm/JDOE/my-repo.git", + "name": "http" + }, + { + "href": "ssh://git@/JDOE/my-repo.git", + "name": "ssh" + } + ], + "self": [ + { + "href": "http://link/to/repository" + } + ] + } +} \ No newline at end of file