diff --git a/.gitignore b/.gitignore index ee080ef..25226e0 100644 --- a/.gitignore +++ b/.gitignore @@ -54,11 +54,13 @@ github-pr-status/build gitlab-mr-status/build gerrit-cs-status/build stash-pr-status/build +gitea-pr-status/build common/out github-pr-status/out gitlab-mr-status/out gerrit-cs-status/out stash-pr-status/out +gitea-pr-status/out # App # diff --git a/README.md b/README.md index 50e3de5..052a441 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Supported: * GitHub Pull Request status * Stash Pull Request status * Gerrit Change Set status +* Gitea Pull Request status ## Requirements These plugins require GoCD version >= v15.x or above @@ -20,7 +21,7 @@ These plugins require GoCD version >= v15.x or above ## Configuration -- You will see `Github Pull Requests status notifier` / `Stash Pull Requests status notifier` / `Gerrit Change Set status notifier` / `GitLab Feature Branch status notifier` on plugin listing page +- You will see `Github Pull Requests status notifier` / `Stash Pull Requests status notifier` / `Gerrit Change Set status notifier` / `GitLab Feature Branch status notifier` / `Gitea Pull Requests status notifier` on plugin listing page ![Plugins listing page][1] - You can configure the plugin (this feature requires GoCD version >= v15.2, use system properties to configure the plugin). The details should be as follows: @@ -115,6 +116,18 @@ Eg: -Dgo.plugin.build.status.gitlab.oauth=XXXX ``` +#### Gitea +**Setup:** +- You need to provide `GoCD Base URL`, `Gitea Base URL`, `Username`, `Password` using the plugin configuration view. +- (or) through system property `go.plugin.build.status.go-server`, `go.plugin.build.status.gitea.endpoint`, `go.plugin.build.status.gitea.username`, `go.plugin.build.status.gitea.password`. +Eg: +``` +-Dgo.plugin.build.status.go-server=https://gitea.com +-Dgo.plugin.build.status.gitea.endpoint=https://gitea.com +-Dgo.plugin.build.status.gitea.username=johndoe +-Dgo.plugin.build.status.gitea.password=thisaintapassword +``` + ## FAQs [1]: images/list-plugin.png "List Plugin" diff --git a/build.gradle b/build.gradle index 9d7d9ac..52d51d1 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,8 @@ gocdPlugin { project(':gerrit-cs-status').tasks.jar, project(':github-pr-status').tasks.jar, project(':gitlab-mr-status').tasks.jar, - project(':stash-pr-status').tasks.jar + project(':stash-pr-status').tasks.jar, + project(':gitea-pr-status').tasks.jar, ] } diff --git a/gerrit-cs-status/src/test/java/com/tw/go/plugin/GerritBuildStatusNotifierPluginTest.java b/gerrit-cs-status/src/test/java/com/tw/go/plugin/GerritBuildStatusNotifierPluginTest.java index 89eb2c0..855802d 100644 --- a/gerrit-cs-status/src/test/java/com/tw/go/plugin/GerritBuildStatusNotifierPluginTest.java +++ b/gerrit-cs-status/src/test/java/com/tw/go/plugin/GerritBuildStatusNotifierPluginTest.java @@ -88,7 +88,7 @@ public void shouldDelegateUpdateStatusToProviderWithCorrectParameters() throws E String expectedPipelineInstance = String.format("%s/%s/%s/%s", pipelineName, pipelineCounter, stageName, stageCounter); String expectedStageResult = "Passed"; - Map requestBody = createRequestBodyMap(expectedURL, expectedUsername, expectedRevision, expectedPRId, pipelineName, pipelineCounter, stageName, stageCounter, expectedStageResult); + Map requestBody = createRequestBodyMap(expectedURL, expectedUsername, expectedRevision, expectedPRId, pipelineName, pipelineCounter, stageName, stageCounter, expectedStageResult); plugin.handleStageNotification(createGoPluginAPIRequest(requestBody)); verify(provider).updateStatus(eq(expectedURL), any(PluginSettings.class), eq("1"), eq(expectedRevision), eq(expectedPipelineStage), eq(expectedStageResult), eq("http://localhost:8153/go/pipelines/" + expectedPipelineInstance)); @@ -145,32 +145,32 @@ public String requestBody() { }; } - private Map createRequestBodyMap(String url, String username, String revision, String prId, String pipelineName, String pipelineCounter, String stageName, String stageCounter, String stageResult) { - Map materialRevisionMap = new HashMap(); - Map materialMap = new HashMap(); + private Map createRequestBodyMap(String url, String username, String revision, String prId, String pipelineName, String pipelineCounter, String stageName, String stageCounter, String stageResult) { + Map materialRevisionMap = new HashMap<>(); + Map materialMap = new HashMap<>(); materialMap.put("type", "scm"); materialMap.put("plugin-id", POLLER_PLUGIN_ID); - Map configurationMap = new HashMap(); + Map configurationMap = new HashMap<>(); configurationMap.put("url", url); configurationMap.put("username", username); materialMap.put("scm-configuration", configurationMap); materialRevisionMap.put("material", materialMap); - List modifications = new ArrayList(); - Map modificationMap = new HashMap(); + List> modifications = new ArrayList<>(); + Map modificationMap = new HashMap<>(); modificationMap.put("revision", revision); - Map modificationDataMap = new HashMap(); + Map modificationDataMap = new HashMap<>(); modificationDataMap.put("PR_ID", prId); modificationMap.put("data", modificationDataMap); modifications.add(modificationMap); materialRevisionMap.put("modifications", modifications); - Map pipelineMap = new HashMap(); - List buildCause = new ArrayList(); + Map pipelineMap = new HashMap<>(); + List> buildCause = new ArrayList<>(); buildCause.add(materialRevisionMap); pipelineMap.put("build-cause", buildCause); - Map stageMap = new HashMap(); + Map stageMap = new HashMap<>(); stageMap.put("name", stageName); stageMap.put("counter", stageCounter); stageMap.put("result", stageResult); @@ -179,12 +179,12 @@ private Map createRequestBodyMap(String url, String username, String revision, S pipelineMap.put("name", pipelineName); pipelineMap.put("counter", pipelineCounter); - Map requestBody = new HashMap(); + Map requestBody = new HashMap<>(); requestBody.put("pipeline", pipelineMap); return requestBody; } - private DefaultGoPluginApiRequest createGoPluginAPIRequest(Map requestBody) { + private DefaultGoPluginApiRequest createGoPluginAPIRequest(Map requestBody) { DefaultGoPluginApiRequest request = new DefaultGoPluginApiRequest(BuildStatusNotifierPlugin.EXTENSION_NAME, "1.0", BuildStatusNotifierPlugin.REQUEST_STAGE_STATUS); request.setRequestBody(JSONUtils.toJSON(requestBody)); return request; diff --git a/gitea-pr-status/build.gradle b/gitea-pr-status/build.gradle new file mode 100644 index 0000000..f1c0ee5 --- /dev/null +++ b/gitea-pr-status/build.gradle @@ -0,0 +1,51 @@ +/* + * Copyright 2022 Thoughtworks, Inc. + * + * Licensed 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. + */ + +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation project(":common") +} + +gocdPlugin { + id = 'gitea.pr.status' + pluginVersion = rootProject.gocdPlugin.pluginVersion + goCdVersion = rootProject.gocdPlugin.goCdVersion + name = 'Gitea Pull Requests status notifier' + description = 'Updates build status for Gitea pull request' + vendorName = rootProject.gocdPlugin.vendorName + vendorUrl = rootProject.gocdPlugin.vendorUrl + + githubRepo { + owner = rootProject.gocdPlugin.githubRepo.owner + repo = rootProject.gocdPlugin.githubRepo.repo + token = rootProject.gocdPlugin.githubRepo.token + } + + pluginProject = project + + prerelease = rootProject.gocdPlugin.prerelease + prereleaseDryrun = rootProject.gocdPlugin.prereleaseDryrun + assetsToRelease = [] // Released by root project +} + +version = gocdPlugin.fullVersion(project) diff --git a/gitea-pr-status/src/main/java/com/tw/go/plugin/GiteaBuildStatusNotifierPlugin.java b/gitea-pr-status/src/main/java/com/tw/go/plugin/GiteaBuildStatusNotifierPlugin.java new file mode 100644 index 0000000..a9c752b --- /dev/null +++ b/gitea-pr-status/src/main/java/com/tw/go/plugin/GiteaBuildStatusNotifierPlugin.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Thoughtworks, Inc. + * + * Licensed 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.tw.go.plugin; + +import com.thoughtworks.go.plugin.api.annotation.Extension; +import com.tw.go.plugin.provider.GiteaProvider; +import com.tw.go.plugin.provider.Provider; + +@Extension +public class GiteaBuildStatusNotifierPlugin extends BuildStatusNotifierPlugin { + @Override + protected Provider loadProvider() { + try { + return new GiteaProvider(); + } catch (Exception e) { + throw new RuntimeException("could not create provider", e); + } + } +} diff --git a/gitea-pr-status/src/main/java/com/tw/go/plugin/provider/GiteaConfigurationView.java b/gitea-pr-status/src/main/java/com/tw/go/plugin/provider/GiteaConfigurationView.java new file mode 100644 index 0000000..d9920d5 --- /dev/null +++ b/gitea-pr-status/src/main/java/com/tw/go/plugin/provider/GiteaConfigurationView.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Thoughtworks, Inc. + * + * Licensed 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.tw.go.plugin.provider; + +import com.tw.go.plugin.setting.PluginConfigurationView; + +import java.util.HashMap; +import java.util.Map; + +import static com.tw.go.plugin.setting.DefaultPluginConfigurationView.*; +import static com.tw.go.plugin.util.ConfigurationUtils.createField; + +public class GiteaConfigurationView implements PluginConfigurationView { + + @Override + public String templateName() { + return "plugin-settings-gitea.template.html"; + } + + @Override + public Map fields() { + Map response = new HashMap<>(); + response.put(PLUGIN_SETTINGS_SERVER_BASE_URL, createField("GoCD Base URL", null, true, false, "0")); + response.put(PLUGIN_SETTINGS_END_POINT, createField("Gitea Base URL", null, true, false, "1")); + response.put(PLUGIN_SETTINGS_USERNAME, createField("Username", null, true, false, "2")); + response.put(PLUGIN_SETTINGS_PASSWORD, createField("Password", null, true, true, "3")); + return response; + } +} diff --git a/gitea-pr-status/src/main/java/com/tw/go/plugin/provider/GiteaProvider.java b/gitea-pr-status/src/main/java/com/tw/go/plugin/provider/GiteaProvider.java new file mode 100644 index 0000000..8eebf54 --- /dev/null +++ b/gitea-pr-status/src/main/java/com/tw/go/plugin/provider/GiteaProvider.java @@ -0,0 +1,174 @@ +/* + * Copyright 2022 Thoughtworks, Inc. + * + * Licensed 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.tw.go.plugin.provider; + +import com.tw.go.plugin.setting.PluginSettings; +import com.tw.go.plugin.util.AuthenticationType; +import com.tw.go.plugin.util.HTTPClient; +import com.tw.go.plugin.util.JSONUtils; +import com.tw.go.plugin.util.ValidationUtils; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.tw.go.plugin.setting.DefaultPluginConfigurationView.*; +import static com.tw.go.plugin.util.ValidationUtils.getValidationError; + +public class GiteaProvider extends DefaultProvider { + public static final String PLUGIN_ID = "gitea.pr.status"; + public static final String GIT_FB_POLLER_PLUGIN_ID = "git.fb"; + public static final String STATUS_CONTEXT = "GoCD"; + + private final HTTPClient httpClient; + + public GiteaProvider() { + super(new GiteaConfigurationView()); + httpClient = new HTTPClient(); + } + + public GiteaProvider(HTTPClient httpClient) { + super(new GiteaConfigurationView()); + this.httpClient = httpClient; + } + + @Override + public String pluginId() { + return PLUGIN_ID; + } + + @Override + public String pollerPluginId() { + return GIT_FB_POLLER_PLUGIN_ID; + } + + @Override + public List> validateConfig(Map fields) { + List> errors = new ArrayList<>(); + + if (!validateUrl(getValueFromPluginSettings(fields.get(PLUGIN_SETTINGS_SERVER_BASE_URL)))) { + errors.add(getValidationError( + PLUGIN_SETTINGS_SERVER_BASE_URL, + "GoCD base url not set correctly" + )); + } + if (!validateUrl(getValueFromPluginSettings(fields.get(PLUGIN_SETTINGS_END_POINT)))) { + errors.add(getValidationError( + PLUGIN_SETTINGS_END_POINT, + "Gitea base url not set correctly" + )); + } + if (getValueFromPluginSettings(fields.get(PLUGIN_SETTINGS_USERNAME)).equals("")) { + errors.add(getValidationError( + PLUGIN_SETTINGS_USERNAME, + "Gitea Username not set correctly" + )); + } + if (getValueFromPluginSettings(fields.get(PLUGIN_SETTINGS_PASSWORD)).equals("")) { + errors.add(getValidationError( + PLUGIN_SETTINGS_PASSWORD, + "Gitea Password not set correctly" + )); + } + + return errors; + } + + @Override + public void updateStatus(String url, PluginSettings pluginSettings, String branch, String revision, String pipelineStage, + String result, String trackbackURL) throws Exception { + String giteaBaseURL = pluginSettings.getEndPoint(); + String username = pluginSettings.getUsername(); + String password = pluginSettings.getPassword(); + + if (ValidationUtils.isEmpty(giteaBaseURL)) { + giteaBaseURL = System.getProperty("go.plugin.build.status.gitea.endpoint"); + } + if (ValidationUtils.isEmpty(username)) { + username = System.getProperty("go.plugin.build.status.gitea.username"); + } + if (ValidationUtils.isEmpty(password)) { + password = System.getProperty("go.plugin.build.status.gitea.password"); + } + + String updateURL = formUpdateURL(giteaBaseURL, url, revision); + Map request = new HashMap<>(); + request.put("context", STATUS_CONTEXT); + request.put("description", GiteaState.descriptionFor(result)); + request.put("state", GiteaState.stateFor(result)); + request.put("target_url", trackbackURL); + String requestBody = JSONUtils.toJSON(request); + + httpClient.postRequest(updateURL, AuthenticationType.BASIC, username, password, requestBody); + } + + private String getValueFromPluginSettings(Object values) { + if (values == null) { + return ""; + } + Map vals = (Map) values; + String ret = vals.get("value"); + if (ret == null) { + return ""; + } + return ret; + } + + private boolean validateUrl(String uri) { + try { + new URL(uri); + } catch (MalformedURLException e) { + return false; + } + return true; + } + + public String formUpdateURL(String gitServerBaseURL, String url, String revision) { + if (gitServerBaseURL.endsWith("/")) { + gitServerBaseURL = gitServerBaseURL.substring(0, gitServerBaseURL.length() - 1); + } + return String.format("%s/api/v1/repos/%s/statuses/%s", gitServerBaseURL, getRepository(url), revision); + } + + public String getRepository(String url) { + String repoPath = null; + + String sshProtocolString = "(.*)@(.*):(.*?)(/*)$"; + Pattern sshPattern = Pattern.compile(sshProtocolString); + Matcher sshMatcher = sshPattern.matcher(url); + if (sshMatcher.find()) { + repoPath = sshMatcher.group(3); + } + + String httpProtocolString = "http(.?)://(.*?)/(.*?)(/*)$"; + Pattern httpPattern = Pattern.compile(httpProtocolString); + Matcher httpMatcher = httpPattern.matcher(url); + if (httpMatcher.find()) { + repoPath = httpMatcher.group(3); + } + + if (!ValidationUtils.isEmpty(repoPath) && repoPath.endsWith(".git")) { + repoPath = repoPath.substring(0, repoPath.length() - 4); + } + return repoPath; + } +} diff --git a/gitea-pr-status/src/main/java/com/tw/go/plugin/provider/GiteaState.java b/gitea-pr-status/src/main/java/com/tw/go/plugin/provider/GiteaState.java new file mode 100644 index 0000000..fff8a32 --- /dev/null +++ b/gitea-pr-status/src/main/java/com/tw/go/plugin/provider/GiteaState.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 Thoughtworks, Inc. + * + * Licensed 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.tw.go.plugin.provider; + +public class GiteaState { + private static final String PENDING = "pending"; + private static final String PENDING_DESCRIPTION = "Build is pending"; + private static final String SUCCESS = "success"; + private static final String SUCCESS_DESCRIPTION = "Build is passing"; + private static final String FAILURE = "failure"; + private static final String FAILURE_DESCRIPTION = "Build is failing"; + private static final String ERROR = "error"; + private static final String ERROR_DESCRIPTION = "Build encountered an error"; + + public GiteaState() { + } + + static String stateFor(String result) { + result = result == null ? "" : result; + String state = PENDING; + if (result.equalsIgnoreCase("Passed")) { + state = SUCCESS; + } else if (result.equalsIgnoreCase("Failed")) { + state = FAILURE; + } else if (result.equalsIgnoreCase("Cancelled")) { + state = ERROR; + } + return state; + } + + static String descriptionFor(String result) { + result = result == null ? "" : result; + String description = PENDING_DESCRIPTION; + if (result.equalsIgnoreCase("Passed")) { + description = SUCCESS_DESCRIPTION; + } else if (result.equalsIgnoreCase("Failed")) { + description = FAILURE_DESCRIPTION; + } else if (result.equalsIgnoreCase("Cancelled")) { + description = ERROR_DESCRIPTION; + } + return description; + } +} diff --git a/gitea-pr-status/src/main/resources/defaults.properties b/gitea-pr-status/src/main/resources/defaults.properties new file mode 100644 index 0000000..fe3f39b --- /dev/null +++ b/gitea-pr-status/src/main/resources/defaults.properties @@ -0,0 +1,17 @@ +# +# Copyright 2022 Thoughtworks, Inc. +# +# Licensed 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. +# + +provider=com.tw.go.plugin.provider.GiteaProvider \ No newline at end of file diff --git a/gitea-pr-status/src/main/resources/plugin-settings-gitea.template.html b/gitea-pr-status/src/main/resources/plugin-settings-gitea.template.html new file mode 100644 index 0000000..07f9f1b --- /dev/null +++ b/gitea-pr-status/src/main/resources/plugin-settings-gitea.template.html @@ -0,0 +1,35 @@ + +
+ + + {{ GOINPUTNAME[server_base_url].$error.server }} +
+
+ + + {{ GOINPUTNAME[end_point].$error.server }} +
+
+ + + {{ GOINPUTNAME[username].$error.server }} +
+
+ + + {{ GOINPUTNAME[password].$error.server }} +
\ No newline at end of file diff --git a/gitea-pr-status/src/test/java/com/tw/go/plugin/GiteaBuildStatusNotifierPluginTest.java b/gitea-pr-status/src/test/java/com/tw/go/plugin/GiteaBuildStatusNotifierPluginTest.java new file mode 100644 index 0000000..bf20b0e --- /dev/null +++ b/gitea-pr-status/src/test/java/com/tw/go/plugin/GiteaBuildStatusNotifierPluginTest.java @@ -0,0 +1,269 @@ +/* + * Copyright 2022 Thoughtworks, Inc. + * + * Licensed 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.tw.go.plugin; + +import com.google.gson.Gson; +import com.thoughtworks.go.plugin.api.GoApplicationAccessor; +import com.thoughtworks.go.plugin.api.request.DefaultGoPluginApiRequest; +import com.thoughtworks.go.plugin.api.request.GoApiRequest; +import com.thoughtworks.go.plugin.api.request.GoPluginApiRequest; +import com.thoughtworks.go.plugin.api.response.DefaultGoApiResponse; +import com.tw.go.plugin.provider.GiteaProvider; +import com.tw.go.plugin.provider.Provider; +import com.tw.go.plugin.setting.PluginConfigurationView; +import com.tw.go.plugin.setting.PluginSettings; +import com.tw.go.plugin.util.JSONUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.tw.go.plugin.BuildStatusNotifierPlugin.PLUGIN_SETTINGS_GET_CONFIGURATION; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.openMocks; + +public class GiteaBuildStatusNotifierPluginTest { + public static final String PLUGIN_ID = "gitea-plugin"; + public static final String POLLER_PLUGIN_ID = "gitea-poller-plugin-id"; + @Mock + private GoApplicationAccessor goApplicationAccessor; + @Mock + private Provider provider; + + private BuildStatusNotifierPlugin plugin; + + @BeforeEach + public void setUp() { + openMocks(this); + + plugin = new GiteaBuildStatusNotifierPlugin(); + + DefaultGoApiResponse pluginSettingsResponse = new DefaultGoApiResponse(200); + pluginSettingsResponse.setResponseBody(JSONUtils.toJSON(new HashMap())); + when(goApplicationAccessor.submit(any(GoApiRequest.class))).thenReturn(pluginSettingsResponse); + when(provider.pluginId()).thenReturn(PLUGIN_ID); + when(provider.pollerPluginId()).thenReturn(POLLER_PLUGIN_ID); + + plugin.initializeGoApplicationAccessor(goApplicationAccessor); + plugin.setProvider(provider); + } + + @Test + public void shouldRegisterForStageStatusChange() { + assertThat(plugin.handleNotificationsInterestedIn().responseBody()).isEqualTo("{\"notifications\":[\"stage-status\"]}"); + } + + @Test + public void shouldDelegateUpdateStatusToProviderWithCorrectParameters() throws Exception { + PluginSettings mockSettings = mock(PluginSettings.class); + when(plugin.getPluginSettings()).thenReturn(mockSettings); + + String expectedURL = "url"; + String expectedUsername = "username"; + String expectedRevision = "sha-1"; + String expectedPRId = "1"; + String pipelineName = "pipeline"; + String pipelineCounter = "1"; + String stageName = "stage"; + String stageCounter = "1"; + String expectedPipelineStage = String.format("%s/%s", pipelineName, stageName); + String expectedPipelineInstance = String.format("%s/%s/%s/%s", pipelineName, pipelineCounter, stageName, stageCounter); + String expectedStageResult = "Passed"; + + Map requestBody = createRequestBodyMap(expectedURL, expectedUsername, expectedRevision, expectedPRId, pipelineName, pipelineCounter, stageName, stageCounter, expectedStageResult); + plugin.handleStageNotification(createGoPluginAPIRequest(requestBody)); + + verify(provider).updateStatus(eq(expectedURL), any(PluginSettings.class), eq("1"), eq(expectedRevision), eq(expectedPipelineStage), eq(expectedStageResult), eq("http://localhost:8153/go/pipelines/" + expectedPipelineInstance)); + } + + @Test + public void shouldDelegateUpdateStatusToProviderWithCorrectParametersWithoutPrID() throws Exception { + PluginSettings mockSettings = mock(PluginSettings.class); + when(plugin.getPluginSettings()).thenReturn(mockSettings); + + String expectedURL = "url"; + String expectedUsername = "username"; + String expectedRevision = "sha-1"; + String expectedBranch = "test-branch"; + String pipelineName = "pipeline"; + String pipelineCounter = "1"; + String stageName = "stage"; + String stageCounter = "1"; + String expectedPipelineStage = String.format("%s/%s", pipelineName, stageName); + String expectedPipelineInstance = String.format("%s/%s/%s/%s", pipelineName, pipelineCounter, stageName, stageCounter); + String expectedStageResult = "Passed"; + + Map requestBody = createRequestBodyMapWithBranch(expectedURL, expectedUsername, expectedRevision, expectedBranch, pipelineName, pipelineCounter, stageName, stageCounter, expectedStageResult); + plugin.handleStageNotification(createGoPluginAPIRequest(requestBody)); + + verify(provider).updateStatus(eq(expectedURL), any(PluginSettings.class), eq("test-branch"), eq(expectedRevision), eq(expectedPipelineStage), eq(expectedStageResult), eq("http://localhost:8153/go/pipelines/" + expectedPipelineInstance)); + } + + @Test + public void shouldReturnPluginSettings() { + Provider mockProvider = mock(Provider.class); + PluginConfigurationView mockConfigView = mock(PluginConfigurationView.class); + when(mockProvider.configurationView()).thenReturn(mockConfigView); + Map fields = new HashMap<>(); + when(mockConfigView.fields()).thenReturn(fields); + + plugin.setProvider(mockProvider); + + Map configuration = new Gson().fromJson( + plugin.handle(createRequest(PLUGIN_SETTINGS_GET_CONFIGURATION) + ).responseBody(), Map.class); + + assertThat(configuration).isEqualTo(fields); + } + + private GoPluginApiRequest createRequest(final String name) { + return new GoPluginApiRequest() { + @Override + public String extension() { + return null; + } + + @Override + public String extensionVersion() { + return null; + } + + @Override + public String requestName() { + return name; + } + + @Override + public Map requestParameters() { + return null; + } + + @Override + public Map requestHeaders() { + return null; + } + + @Override + public String requestBody() { + return null; + } + }; + } + + private Map createRequestBodyMap(String url, String username, String revision, String prId, String pipelineName, String pipelineCounter, String stageName, String stageCounter, String stageResult) { + Map materialRevisionMap = new HashMap<>(); + Map materialMap = new HashMap<>(); + materialMap.put("type", "scm"); + materialMap.put("plugin-id", POLLER_PLUGIN_ID); + Map configurationMap = new HashMap<>(); + configurationMap.put("url", url); + configurationMap.put("username", username); + materialMap.put("scm-configuration", configurationMap); + materialRevisionMap.put("material", materialMap); + + List> modifications = new ArrayList<>(); + Map modificationMap = new HashMap<>(); + modificationMap.put("revision", revision); + Map modificationDataMap = new HashMap<>(); + modificationDataMap.put("PR_ID", prId); + modificationMap.put("data", modificationDataMap); + modifications.add(modificationMap); + materialRevisionMap.put("modifications", modifications); + + Map pipelineMap = new HashMap<>(); + List> buildCause = new ArrayList<>(); + buildCause.add(materialRevisionMap); + pipelineMap.put("build-cause", buildCause); + + Map stageMap = new HashMap<>(); + stageMap.put("name", stageName); + stageMap.put("counter", stageCounter); + stageMap.put("result", stageResult); + pipelineMap.put("stage", stageMap); + + pipelineMap.put("name", pipelineName); + pipelineMap.put("counter", pipelineCounter); + + Map requestBody = new HashMap<>(); + requestBody.put("pipeline", pipelineMap); + return requestBody; + } + + @Test + public void shouldReturnCorrectConfigForGiteaPlugin() { + plugin.setProvider(new GiteaProvider()); + + Map configuration = new Gson().fromJson(plugin.handle(createRequest(PLUGIN_SETTINGS_GET_CONFIGURATION)).responseBody(), Map.class); + + assertThat(configuration.containsKey("server_base_url")).isEqualTo(true); + assertThat(configuration.containsKey("end_point")).isEqualTo(true); + assertThat(configuration.containsKey("username")).isEqualTo(true); + assertThat(configuration.containsKey("password")).isEqualTo(true); + assertThat(configuration.containsKey("oauth_token")).isEqualTo(false); + assertThat(configuration.containsKey("review_label")).isEqualTo(false); + } + + private Map createRequestBodyMapWithBranch(String url, String username, String revision, String branch, String pipelineName, String pipelineCounter, String stageName, String stageCounter, String stageResult) { + Map materialRevisionMap = new HashMap<>(); + Map materialMap = new HashMap<>(); + materialMap.put("type", "scm"); + materialMap.put("plugin-id", POLLER_PLUGIN_ID); + Map configurationMap = new HashMap<>(); + configurationMap.put("url", url); + configurationMap.put("username", username); + materialMap.put("scm-configuration", configurationMap); + materialRevisionMap.put("material", materialMap); + + List> modifications = new ArrayList<>(); + Map modificationMap = new HashMap<>(); + modificationMap.put("revision", revision); + Map modificationDataMap = new HashMap<>(); + modificationDataMap.put("CURRENT_BRANCH", branch); + modificationMap.put("data", modificationDataMap); + modifications.add(modificationMap); + materialRevisionMap.put("modifications", modifications); + + Map pipelineMap = new HashMap<>(); + List> buildCause = new ArrayList<>(); + buildCause.add(materialRevisionMap); + pipelineMap.put("build-cause", buildCause); + + Map stageMap = new HashMap<>(); + stageMap.put("name", stageName); + stageMap.put("counter", stageCounter); + stageMap.put("result", stageResult); + pipelineMap.put("stage", stageMap); + + pipelineMap.put("name", pipelineName); + pipelineMap.put("counter", pipelineCounter); + + Map requestBody = new HashMap<>(); + requestBody.put("pipeline", pipelineMap); + return requestBody; + } + + private DefaultGoPluginApiRequest createGoPluginAPIRequest(Map requestBody) { + DefaultGoPluginApiRequest request = new DefaultGoPluginApiRequest(BuildStatusNotifierPlugin.EXTENSION_NAME, "1.0", BuildStatusNotifierPlugin.REQUEST_STAGE_STATUS); + request.setRequestBody(JSONUtils.toJSON(requestBody)); + return request; + } +} diff --git a/gitea-pr-status/src/test/java/com/tw/go/plugin/provider/GiteaConfigurationViewTest.java b/gitea-pr-status/src/test/java/com/tw/go/plugin/provider/GiteaConfigurationViewTest.java new file mode 100644 index 0000000..9290602 --- /dev/null +++ b/gitea-pr-status/src/test/java/com/tw/go/plugin/provider/GiteaConfigurationViewTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2022 Thoughtworks, Inc. + * + * Licensed 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.tw.go.plugin.provider; + + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; + +import static com.tw.go.plugin.setting.DefaultPluginConfigurationView.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +public class GiteaConfigurationViewTest { + + private GiteaConfigurationView view; + + @BeforeEach + public void setUp() { + view = new GiteaConfigurationView(); + } + + @Test + public void checkExpectedFields() { + Set expected = new HashSet<>(); + expected.add(PLUGIN_SETTINGS_SERVER_BASE_URL); + expected.add(PLUGIN_SETTINGS_END_POINT); + expected.add(PLUGIN_SETTINGS_USERNAME); + expected.add(PLUGIN_SETTINGS_PASSWORD); + assertEquals(expected, view.fields().keySet()); + } + + @Test + public void checkTemplateName() { + assertEquals("plugin-settings-gitea.template.html", view.templateName()); + } +} diff --git a/gitea-pr-status/src/test/java/com/tw/go/plugin/provider/GiteaProviderTest.java b/gitea-pr-status/src/test/java/com/tw/go/plugin/provider/GiteaProviderTest.java new file mode 100644 index 0000000..95925b0 --- /dev/null +++ b/gitea-pr-status/src/test/java/com/tw/go/plugin/provider/GiteaProviderTest.java @@ -0,0 +1,158 @@ +/* + * Copyright 2022 Thoughtworks, Inc. + * + * Licensed 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.tw.go.plugin.provider; + +import com.tw.go.plugin.setting.DefaultPluginSettings; +import com.tw.go.plugin.util.AuthenticationType; +import com.tw.go.plugin.util.HTTPClient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static com.tw.go.plugin.setting.DefaultPluginConfigurationView.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class GiteaProviderTest { + DefaultPluginSettings pluginSettings; + HTTPClient mockHttpClient; + GiteaProvider provider; + + private static final String serverBaseURL = "https://gocd-server.com"; + private static final String endpoint = "https://www.test-gitea.com"; + private static final String testUser = "testUser"; + private static final String testPassword = "testPassword"; + + @BeforeEach + public void setUp() { + pluginSettings = new DefaultPluginSettings(); + pluginSettings.setServerBaseURL(serverBaseURL); + pluginSettings.setEndPoint(endpoint); + pluginSettings.setUsername(testUser); + pluginSettings.setPassword(testPassword); + mockHttpClient = mock(HTTPClient.class); + + provider = new GiteaProvider(mockHttpClient); + } + + @Test + public void checkIdsMatch() { + assertEquals("gitea.pr.status", provider.pluginId()); + assertEquals("git.fb", provider.pollerPluginId()); + } + + @Test + public void checkValidationWithValidValues() { + Map config = new LinkedHashMap<>(); + List> errors = new ArrayList<>(); + + Map dummyUrl = new LinkedHashMap<>(); + dummyUrl.put("value", "http://localhost:8153"); + + Map dummyUsername = new LinkedHashMap<>(); + dummyUsername.put("value", "testUser"); + + Map dummyPassword = new LinkedHashMap<>(); + dummyPassword.put("value", "testPassword"); + + config.put(PLUGIN_SETTINGS_SERVER_BASE_URL, dummyUrl); + config.put(PLUGIN_SETTINGS_END_POINT, dummyUrl); + config.put(PLUGIN_SETTINGS_USERNAME, dummyUsername); + config.put(PLUGIN_SETTINGS_PASSWORD, dummyPassword); + + List> returnedErrors = provider.validateConfig(config); + assertEquals(errors, returnedErrors); + } + + @Test + public void checkValidationWithInvalidValues() { + Map config = new LinkedHashMap<>(); + + Map dummyUrl = new LinkedHashMap<>(); + dummyUrl.put("value", "invalid"); + + Map dummyUsername = new LinkedHashMap<>(); + dummyUsername.put("value", ""); + + Map dummyPassword = new LinkedHashMap<>(); + dummyPassword.put("value", ""); + + config.put(PLUGIN_SETTINGS_SERVER_BASE_URL, dummyUrl); + config.put(PLUGIN_SETTINGS_END_POINT, dummyUrl); + config.put(PLUGIN_SETTINGS_USERNAME, dummyUsername); + config.put(PLUGIN_SETTINGS_PASSWORD, dummyPassword); + + List> returnedErrors = provider.validateConfig(config); + assertEquals(4, returnedErrors.size()); + } + + @Test + public void shouldGetRepositoryFromURL() { + assertThat(provider.getRepository("http://gitea.com/group/sample-repo")).isEqualTo("group/sample-repo"); + assertThat(provider.getRepository("http://gitea.com/group/sample-repo.git")).isEqualTo("group/sample-repo"); + assertThat(provider.getRepository("http://gitea.com/group/sample-repo/")).isEqualTo("group/sample-repo"); + assertThat(provider.getRepository("http://gitea.com/group/sample-repo.git/")).isEqualTo("group/sample-repo"); + assertThat(provider.getRepository("https://gitea.com/group/sample-repo")).isEqualTo("group/sample-repo"); + assertThat(provider.getRepository("https://gitea.com/group/sample-repo.git")).isEqualTo("group/sample-repo"); + assertThat(provider.getRepository("git@code.corp.yourcompany.com:group/sample-repo")).isEqualTo("group/sample-repo"); + assertThat(provider.getRepository("git@code.corp.yourcompany.com:group/sample-repo.git")).isEqualTo("group/sample-repo"); + assertThat(provider.getRepository("git@gitea.com:group/sample-repo.git")).isEqualTo("group/sample-repo"); + assertThat(provider.getRepository("http://gitea.com/group/sub-group/sample-repo.git/")).isEqualTo("group/sub-group/sample-repo"); + assertThat(provider.getRepository("https://gitea.com/group/sub-group/another-sub-group/sample-repo")).isEqualTo("group/sub-group/another-sub-group/sample-repo"); + } + + @Test + public void checkFormUpdateURL() { + String gitServerBaseURL = "https://www.test-gitea.com"; + String gitServerBaseURLWithEndingSlash = "https://www.test-gitea.com/"; + String repositoryURL = "https://www.test-gitea.com/test-owner/test-repo"; + String commitRevision = "revValue"; + + assertThat(provider.formUpdateURL(gitServerBaseURL, repositoryURL, commitRevision)).isEqualTo("https://www.test-gitea.com/api/v1/repos/test-owner/test-repo/statuses/revValue"); + assertThat(provider.formUpdateURL(gitServerBaseURLWithEndingSlash, repositoryURL, commitRevision)).isEqualTo("https://www.test-gitea.com/api/v1/repos/test-owner/test-repo/statuses/revValue"); + } + + @Test + public void shouldUpdateStatusForPR() throws Exception { + provider.updateStatus("https://www.test-gitea.com/test-owner/test-repo", + pluginSettings, + "test-branch", + "revValue", + "pipeline-name/stage-name", + "Passed", + "http://localhost:8153/go/pipelines/pipeline-name/1/stage-name/1"); + + verify(mockHttpClient).postRequest( + "https://www.test-gitea.com/api/v1/repos/test-owner/test-repo/statuses/revValue", + AuthenticationType.BASIC, + testUser, + testPassword, + "{" + + "\"target_url\":\"http://localhost:8153/go/pipelines/pipeline-name/1/stage-name/1\"," + + "\"context\":\"GoCD\"," + + "\"description\":\"Build is passing\"," + + "\"state\":\"success\"" + + "}" + ); + } +} diff --git a/gitea-pr-status/src/test/java/com/tw/go/plugin/provider/GiteaStateTest.java b/gitea-pr-status/src/test/java/com/tw/go/plugin/provider/GiteaStateTest.java new file mode 100644 index 0000000..c0744f3 --- /dev/null +++ b/gitea-pr-status/src/test/java/com/tw/go/plugin/provider/GiteaStateTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Thoughtworks, Inc. + * + * Licensed 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.tw.go.plugin.provider; + + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class GiteaStateTest { + @Test + public void checkStates() { + assertEquals("pending", GiteaState.stateFor("unknown")); + assertEquals("pending", GiteaState.stateFor(null)); + assertEquals("success", GiteaState.stateFor("Passed")); + assertEquals("failure", GiteaState.stateFor("Failed")); + assertEquals("error", GiteaState.stateFor("Cancelled")); + } + + @Test + public void checkDescriptions() { + assertEquals("Build is pending", GiteaState.descriptionFor("unknown")); + assertEquals("Build is pending", GiteaState.descriptionFor(null)); + assertEquals("Build is passing", GiteaState.descriptionFor("Passed")); + assertEquals("Build is failing", GiteaState.descriptionFor("Failed")); + assertEquals("Build encountered an error", GiteaState.descriptionFor("Cancelled")); + } +} diff --git a/settings.gradle b/settings.gradle index 5fb4207..3969026 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,3 +19,4 @@ include 'github-pr-status' include 'stash-pr-status' include 'gerrit-cs-status' include 'gitlab-mr-status' +include 'gitea-pr-status'