From 627449ec63308cb83bdcaa00353c2f4e3b5a8b3a Mon Sep 17 00:00:00 2001 From: RecuencoJones Date: Sat, 21 Mar 2020 00:42:54 +0100 Subject: [PATCH 1/8] ADD active object --- README | 4 ++ pom.xml | 16 +++++ .../log/RepositoryCloneSettingsServlet.java | 2 +- .../log/dao/RepositoryCloneSettings.java | 30 ++++++++ .../log/dao/RepositoryCloneSettingsDAO.java | 68 +++++++++++++++++++ .../log/rest/RepositoryCloneSettingsDTO.java | 21 ++++++ .../rest/RepositoryCloneSettingsResource.java | 23 ++++++- .../spring/atlassian-component-imports.xml | 12 ++++ src/main/resources/atlassian-plugin.xml | 5 ++ .../resources/static/clone-log-settings.js | 9 ++- 10 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettings.java create mode 100644 src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java create mode 100644 src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java create mode 100644 src/main/resources/META-INF/spring/atlassian-component-imports.xml diff --git a/README b/README index f750230..1dbfe2f 100644 --- a/README +++ b/README @@ -20,3 +20,7 @@ Useful resources: * [SAL 3.0 Upgrade Guide](https://developer.atlassian.com/server/framework/atlassian-sdk/sal-3-0-upgrade-guide/) * [Atlassian Community - REST requests from a custom plugin](https://community.atlassian.com/t5/Answers-Developer-Questions/What-is-the-recommended-way-to-send-REST-requests-from-a-custom/qaq-p/568227) * [Atlassian AUI - Soy template components](https://bitbucket.org/atlassian/aui/src/6a8e37f85a07952cc290503f54d85ff5bebc8783/src/soy/?at=5.10.x) + +Code samples: + +* https://bitbucket.org/atlassian/bitbucket-code-coverage/src/master/code-coverage-plugin/ diff --git a/pom.xml b/pom.xml index 77c3f50..7ac0cd2 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,11 @@ bitbucket-spi provided + + com.atlassian.activeobjects + activeobjects-plugin + provided + com.atlassian.soy soy-template-renderer-api @@ -82,6 +87,17 @@ provided + + org.springframework + spring-beans + provided + + + org.springframework + spring-context + provided + + com.atlassian.plugin atlassian-spring-scanner-annotation diff --git a/src/main/java/com/recuencojones/bitbucket/log/RepositoryCloneSettingsServlet.java b/src/main/java/com/recuencojones/bitbucket/log/RepositoryCloneSettingsServlet.java index f28d84c..3afd62d 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/RepositoryCloneSettingsServlet.java +++ b/src/main/java/com/recuencojones/bitbucket/log/RepositoryCloneSettingsServlet.java @@ -29,7 +29,7 @@ import java.util.Map; -class RepositoryCloneSettingsServlet extends HttpServlet { +public class RepositoryCloneSettingsServlet extends HttpServlet { private static final Logger log = LoggerFactory.getLogger(RepositoryCloneSettingsServlet.class); @ComponentImport diff --git a/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettings.java b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettings.java new file mode 100644 index 0000000..ad9be8d --- /dev/null +++ b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettings.java @@ -0,0 +1,30 @@ +package com.recuencojones.bitbucket.log.dao; + +import net.java.ao.Accessor; +import net.java.ao.Entity; +import net.java.ao.Mutator; +import net.java.ao.schema.Indexed; +import net.java.ao.schema.NotNull; +import net.java.ao.schema.StringLength; +import net.java.ao.schema.Table; + +import static net.java.ao.schema.StringLength.UNLIMITED; + +@Table("RC_SETTINGS") +public interface RepositoryCloneSettings extends Entity { + + String REPO_ID_COLUMN = "REPO_ID"; + String URL_COLUMN = "URL"; + + @Indexed + @Accessor(REPO_ID_COLUMN) + @NotNull + int getId(); + + @Accessor(URL_COLUMN) + @StringLength(UNLIMITED) + String getURL(); + + @Mutator(URL_COLUMN) + void setURL(String url); +} diff --git a/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java new file mode 100644 index 0000000..51bbee1 --- /dev/null +++ b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java @@ -0,0 +1,68 @@ +package com.recuencojones.bitbucket.log.dao; + +import com.atlassian.activeobjects.external.ActiveObjects; + +import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; +import org.springframework.stereotype.Component; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.java.ao.DBParam; + +import javax.inject.Inject; +import javax.inject.Named; + +import static com.recuencojones.bitbucket.log.dao.RepositoryCloneSettings.REPO_ID_COLUMN; + +@Named +@Component +public class RepositoryCloneSettingsDAO { + private static final String ID_QUERY = String.format("%s = ?", REPO_ID_COLUMN); + + @ComponentImport + private final ActiveObjects ao; + + @Inject + public RepositoryCloneSettingsDAO( + ActiveObjects ao + ) { + this.ao = ao; + } + + public RepositoryCloneSettings save(int repositoryID, String url) { + RepositoryCloneSettings settings = find(repositoryID); + + if (settings == null) { + settings = ao.create( + RepositoryCloneSettings.class, + new DBParam(REPO_ID_COLUMN, repositoryID) + ); + } + + settings.setURL(url); + settings.save(); + + return settings; + } + + public RepositoryCloneSettings get(int repositoryID) { + return find(repositoryID); + } + + public void remove(int repositoryID) { + RepositoryCloneSettings settings = find(repositoryID); + + ao.delete(settings); + } + + private RepositoryCloneSettings find(int repositoryID) { + RepositoryCloneSettings[] results = ao.find(RepositoryCloneSettings.class, ID_QUERY, repositoryID); + + if (results.length == 0) { + return null; + } + + return results[0]; + } +} diff --git a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java new file mode 100644 index 0000000..1a785bf --- /dev/null +++ b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java @@ -0,0 +1,21 @@ +package com.recuencojones.bitbucket.log.rest; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.annotate.JsonProperty; + +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class RepositoryCloneSettingsDTO { + + @JsonProperty("url") + private String url; + + public String getURL() { + return url; + } + + public void setURL(String url) { + this.url = url; + } +} diff --git a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java index 1231324..66a9e93 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java +++ b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java @@ -1,5 +1,8 @@ package com.recuencojones.bitbucket.log.rest; +import com.recuencojones.bitbucket.log.rest.*; +import com.recuencojones.bitbucket.log.dao.*; + import com.atlassian.bitbucket.repository.Repository; import com.atlassian.bitbucket.rest.util.ResourcePatterns; import com.atlassian.bitbucket.rest.util.ResponseFactory; @@ -34,26 +37,40 @@ public class RepositoryCloneSettingsResource { @ComponentImport private final PermissionValidationService permissionValidationService; + private final RepositoryCloneSettingsDAO repositoryCloneSettingsDAO; + @Inject public RepositoryCloneSettingsResource( - PermissionValidationService permissionValidationService + PermissionValidationService permissionValidationService, + RepositoryCloneSettingsDAO repositoryCloneSettingsDAO ) { this.permissionValidationService = permissionValidationService; + this.repositoryCloneSettingsDAO = repositoryCloneSettingsDAO; } @GET public Response getSettings(@Context Repository repository) { log.info("Retrieve logging url for repository {}/{}", repository.getProject().getKey(), repository.getSlug()); - // return ResponseFactory.ok(new RestAutoUnapproveSettings(unapproveService.getSettings(scope))).build(); + RepositoryCloneSettings settings = repositoryCloneSettingsDAO.get(repository.getId()); + + if (settings != null) { + RepositoryCloneSettingsDTO settingsDTO = new RepositoryCloneSettingsDTO(); + + settingsDTO.setURL(settings.getURL()); + + return ResponseFactory.ok(settingsDTO).build(); + } + return ResponseFactory.ok().build(); } @POST - public Response saveSettings(@Context Repository repository) { + public Response saveSettings(@Context Repository repository, RepositoryCloneSettingsDTO settings) { permissionValidationService.validateForRepository(repository, Permission.REPO_ADMIN); log.info("Store logging url for repository {}/{}", repository.getProject().getKey(), repository.getSlug()); + repositoryCloneSettingsDAO.save(repository.getId(), settings.getURL()); return ResponseFactory.ok().build(); } diff --git a/src/main/resources/META-INF/spring/atlassian-component-imports.xml b/src/main/resources/META-INF/spring/atlassian-component-imports.xml new file mode 100644 index 0000000..f749f0e --- /dev/null +++ b/src/main/resources/META-INF/spring/atlassian-component-imports.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/main/resources/atlassian-plugin.xml b/src/main/resources/atlassian-plugin.xml index 3182ad5..5d175c1 100644 --- a/src/main/resources/atlassian-plugin.xml +++ b/src/main/resources/atlassian-plugin.xml @@ -31,6 +31,11 @@ com.recuencojones.bitbucket.log.rest + + The module configuring the Active Objects service used by this plugin + com.recuencojones.bitbucket.log.dao.RepositoryCloneSettings + + /**/*.soy diff --git a/src/main/resources/static/clone-log-settings.js b/src/main/resources/static/clone-log-settings.js index f575100..fbb69c8 100644 --- a/src/main/resources/static/clone-log-settings.js +++ b/src/main/resources/static/clone-log-settings.js @@ -84,15 +84,18 @@ define('plugin/log-on-clone/repository-settings', [ url: resourceUrl('settings'), type: 'GET' }) - .then(function() { + .then(function(response) { + if (response) { + setValue(response.url); + } + setInputEnabled(true); setSubmitEnabled(true); - setValue('https://en6qhxx7a3ksl.x.pipedream.net'); }); } exports.onReady = function() { - $(document).ready(() => { + $(document).ready(function() { init(); }); }; From 42d8a442673b207ecb767ad4e6e5e9557491608b Mon Sep 17 00:00:00 2001 From: RecuencoJones Date: Sat, 21 Mar 2020 17:46:17 +0100 Subject: [PATCH 2/8] UPDATE template, add enabled checkbox --- pom.xml | 2 + .../bitbucket/log/OnRepositoryClone.java | 43 +++++++----- .../bitbucket/log/OnRepositoryDeleted.java | 26 ++++--- .../log/dao/RepositoryCloneSettings.java | 7 ++ .../log/dao/RepositoryCloneSettingsDAO.java | 9 ++- .../log/rest/RepositoryCloneSettingsDTO.java | 27 +++++--- .../rest/RepositoryCloneSettingsResource.java | 3 +- .../spring/atlassian-component-imports.xml | 12 ---- src/main/resources/log-on-clone.properties | 2 + .../resources/static/clone-log-settings.css | 7 ++ .../resources/static/clone-log-settings.js | 67 ++++++++++++------- .../resources/static/clone-log-settings.soy | 17 +++++ 12 files changed, 146 insertions(+), 76 deletions(-) delete mode 100644 src/main/resources/META-INF/spring/atlassian-component-imports.xml diff --git a/pom.xml b/pom.xml index 7ac0cd2..94512c5 100644 --- a/pom.xml +++ b/pom.xml @@ -172,6 +172,8 @@ + com.recuencojones.bitbucket.log, + com.recuencojones.bitbucket.log.dao, com.recuencojones.bitbucket.log.api, diff --git a/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryClone.java b/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryClone.java index 18843d6..85231ee 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryClone.java +++ b/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryClone.java @@ -1,8 +1,11 @@ package com.recuencojones.bitbucket.log; +import com.recuencojones.bitbucket.log.dao.*; + import com.atlassian.bitbucket.event.repository.RepositoryCloneEvent; import com.atlassian.bitbucket.repository.Repository; import com.atlassian.event.api.EventListener; +import com.atlassian.event.api.EventPublisher; import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; import com.atlassian.sal.api.net.*; @@ -14,44 +17,52 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.inject.Named; +import org.springframework.stereotype.Component; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; + import javax.inject.Inject; +import javax.inject.Named; -@Named("onRepositoryClone") +@Named public class OnRepositoryClone { private static final Logger log = LoggerFactory.getLogger(OnRepositoryClone.class); @ComponentImport private final RequestFactory requestFactory; - @ComponentImport - private final PluginSettingsFactory pluginSettingsFactory; + private final RepositoryCloneSettingsDAO repositoryCloneSettingsDAO; @Inject public OnRepositoryClone( final RequestFactory requestFactory, - final PluginSettingsFactory pluginSettingsFactory + final RepositoryCloneSettingsDAO repositoryCloneSettingsDAO ) { this.requestFactory = requestFactory; - this.pluginSettingsFactory = pluginSettingsFactory; + this.repositoryCloneSettingsDAO = repositoryCloneSettingsDAO; } @EventListener - public void onCloneEvent(final RepositoryCloneEvent cloneEvent) { - final Repository repository = cloneEvent.getRepository(); - final String projectKey = repository.getProject().getKey(); + public void onCloneEvent(final RepositoryCloneEvent event) { + final Repository repository = event.getRepository(); + final int repositoryID = repository.getId(); final String repositorySlug = repository.getSlug(); + final String projectKey = repository.getProject().getKey(); + + final RepositoryCloneSettings settings = repositoryCloneSettingsDAO.get(repositoryID); - log.info("Repository {}/{} cloned", projectKey, repositorySlug); + if (settings != null && settings.isEnabled()) { + log.debug("Repository {}/{} has log-on-clone settings", projectKey, repositorySlug); - final Request request = requestFactory.createRequest(Request.MethodType.POST, "https://en6qhxx7a3ksl.x.pipedream.net"); + final Request request = requestFactory.createRequest(Request.MethodType.POST, settings.getURL()); - request.setRequestBody(new Gson().toJson(repository)); + request.setRequestBody(new Gson().toJson(repository)); - try { - request.execute(); - } catch (final ResponseException e) { - log.error("Could not log clone of {}/{}. Skipping.", projectKey, repositorySlug); + try { + request.execute(); + } catch (final ResponseException e) { + log.error("Could not log clone of {}/{}. Skipping.", projectKey, repositorySlug); + } } } } diff --git a/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryDeleted.java b/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryDeleted.java index a564186..d7b7864 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryDeleted.java +++ b/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryDeleted.java @@ -1,5 +1,7 @@ package com.recuencojones.bitbucket.log; +import com.recuencojones.bitbucket.log.dao.*; + import com.atlassian.bitbucket.event.repository.RepositoryDeletedEvent; import com.atlassian.bitbucket.repository.Repository; @@ -17,26 +19,30 @@ import javax.inject.Named; import javax.inject.Inject; -@Named("onRepositoryDeleted") +@Named public class OnRepositoryDeleted { private static final Logger log = LoggerFactory.getLogger(OnRepositoryClone.class); - @ComponentImport - private final PluginSettingsFactory pluginSettingsFactory; + private final RepositoryCloneSettingsDAO repositoryCloneSettingsDAO; @Inject public OnRepositoryDeleted( - final PluginSettingsFactory pluginSettingsFactory + final RepositoryCloneSettingsDAO repositoryCloneSettingsDAO ) { - this.pluginSettingsFactory = pluginSettingsFactory; + this.repositoryCloneSettingsDAO = repositoryCloneSettingsDAO; } @EventListener public void onRepositoryDeleted(final RepositoryDeletedEvent event) { - deleteSettings(event.getRepository()); - } - - private void deleteSettings(final Repository repository) { - + final Repository repository = event.getRepository(); + final int repositoryID = repository.getId(); + final String repositorySlug = repository.getSlug(); + final String projectKey = repository.getProject().getKey(); + final RepositoryCloneSettings settings = repositoryCloneSettingsDAO.get(repositoryID); + + if (settings != null) { + log.debug("Repository {}/{} has log-on-clone settings, delete.", projectKey, repositorySlug); + repositoryCloneSettingsDAO.remove(settings); + } } } diff --git a/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettings.java b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettings.java index ad9be8d..db240ff 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettings.java +++ b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettings.java @@ -14,6 +14,7 @@ public interface RepositoryCloneSettings extends Entity { String REPO_ID_COLUMN = "REPO_ID"; + String ENABLED_COLUMN = "ENABLED"; String URL_COLUMN = "URL"; @Indexed @@ -21,6 +22,12 @@ public interface RepositoryCloneSettings extends Entity { @NotNull int getId(); + @Accessor(ENABLED_COLUMN) + boolean isEnabled(); + + @Mutator(ENABLED_COLUMN) + void setEnabled(boolean enabled); + @Accessor(URL_COLUMN) @StringLength(UNLIMITED) String getURL(); diff --git a/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java index 51bbee1..b92029e 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java +++ b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java @@ -11,11 +11,9 @@ import net.java.ao.DBParam; import javax.inject.Inject; -import javax.inject.Named; import static com.recuencojones.bitbucket.log.dao.RepositoryCloneSettings.REPO_ID_COLUMN; -@Named @Component public class RepositoryCloneSettingsDAO { private static final String ID_QUERY = String.format("%s = ?", REPO_ID_COLUMN); @@ -30,7 +28,7 @@ public RepositoryCloneSettingsDAO( this.ao = ao; } - public RepositoryCloneSettings save(int repositoryID, String url) { + public RepositoryCloneSettings save(int repositoryID, String url, boolean enabled) { RepositoryCloneSettings settings = find(repositoryID); if (settings == null) { @@ -41,6 +39,7 @@ public RepositoryCloneSettings save(int repositoryID, String url) { } settings.setURL(url); + settings.setEnabled(enabled); settings.save(); return settings; @@ -53,6 +52,10 @@ public RepositoryCloneSettings get(int repositoryID) { public void remove(int repositoryID) { RepositoryCloneSettings settings = find(repositoryID); + remove(settings); + } + + public void remove(RepositoryCloneSettings settings) { ao.delete(settings); } diff --git a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java index 1a785bf..a03080a 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java +++ b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java @@ -8,14 +8,25 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class RepositoryCloneSettingsDTO { - @JsonProperty("url") - private String url; + @JsonProperty("enabled") + private boolean enabled; - public String getURL() { - return url; - } + @JsonProperty("url") + private String url; - public void setURL(String url) { - this.url = url; - } + public boolean getEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getURL() { + return url; + } + + public void setURL(String url) { + this.url = url; + } } diff --git a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java index 66a9e93..f36d3d0 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java +++ b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java @@ -57,6 +57,7 @@ public Response getSettings(@Context Repository repository) { if (settings != null) { RepositoryCloneSettingsDTO settingsDTO = new RepositoryCloneSettingsDTO(); + settingsDTO.setEnabled(settings.isEnabled()); settingsDTO.setURL(settings.getURL()); return ResponseFactory.ok(settingsDTO).build(); @@ -70,7 +71,7 @@ public Response saveSettings(@Context Repository repository, RepositoryCloneSett permissionValidationService.validateForRepository(repository, Permission.REPO_ADMIN); log.info("Store logging url for repository {}/{}", repository.getProject().getKey(), repository.getSlug()); - repositoryCloneSettingsDAO.save(repository.getId(), settings.getURL()); + repositoryCloneSettingsDAO.save(repository.getId(), settings.getURL(), settings.getEnabled()); return ResponseFactory.ok().build(); } diff --git a/src/main/resources/META-INF/spring/atlassian-component-imports.xml b/src/main/resources/META-INF/spring/atlassian-component-imports.xml deleted file mode 100644 index f749f0e..0000000 --- a/src/main/resources/META-INF/spring/atlassian-component-imports.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/src/main/resources/log-on-clone.properties b/src/main/resources/log-on-clone.properties index fe8d29c..65c4f3c 100644 --- a/src/main/resources/log-on-clone.properties +++ b/src/main/resources/log-on-clone.properties @@ -1,5 +1,7 @@ bitbucket.web.repository.log-on-clone.title=Log on clone for {0} / {1} bitbucket.web.repository.log-on-clone.heading=Log on clone +bitbucket.web.repository.log-on-clone.setting.enabled.label=Enable +bitbucket.web.repository.log-on-clone.setting.enabled.description=Call URL on clone event? bitbucket.web.repository.log-on-clone.setting.url.label=URL bitbucket.web.repository.log-on-clone.setting.url.placeholder=Logging service URL bitbucket.web.repository.log-on-clone.setting.url.description=Target url should be a webhook listening on http/https. Event will be sent with POST method. diff --git a/src/main/resources/static/clone-log-settings.css b/src/main/resources/static/clone-log-settings.css index 745dc00..69a3c38 100644 --- a/src/main/resources/static/clone-log-settings.css +++ b/src/main/resources/static/clone-log-settings.css @@ -6,3 +6,10 @@ width: 20px; top: 5px; } + +.button-success { + display: inline-block; + top: -12px; + margin-left: 8px; + color: #35B37E; +} diff --git a/src/main/resources/static/clone-log-settings.js b/src/main/resources/static/clone-log-settings.js index fbb69c8..12fcbe2 100644 --- a/src/main/resources/static/clone-log-settings.js +++ b/src/main/resources/static/clone-log-settings.js @@ -1,11 +1,9 @@ define('plugin/log-on-clone/repository-settings', [ 'jquery', - 'aui', 'bitbucket/util/server', 'bitbucket/util/navbuilder', - 'bitbucket/util/state', 'exports' -], function($, AJS, server, nav, pageState, exports) { +], function($, server, nav, exports) { function resourceUrl(resourceName) { return nav .rest('log-on-clone') @@ -15,14 +13,18 @@ define('plugin/log-on-clone/repository-settings', [ } var formSelector = '#log-on-clone-settings-form'; - var inputSelector = 'input#url'; + var urlInputSelector = 'input#url'; + var enabledInputSelector = 'input#enabled'; + var successTemplate = 'Done!'; var spinnerTemplate = '
'; function init() { var $form = $(formSelector); - var $input = $(inputSelector); + var $urlInput = $(urlInputSelector); + var $enabledInput = $(enabledInputSelector); var $submit = $form.find('#log-on-clone-settings-form-submit'); var $spinner; + var $success; function addSpinner() { $spinner = $(spinnerTemplate).insertAfter($submit); @@ -34,50 +36,60 @@ define('plugin/log-on-clone/repository-settings', [ $spinner = null; } - function setInputEnabled(enabled) { - if (enabled) { - $input.removeAttr('disabled').removeClass('disabled'); - } else { - $input.attr('disabled', 'disabled').addClass('disabled'); - } + function flashSuccess() { + $success = $(successTemplate).insertAfter($submit); + setTimeout(function() { + $success && $success.remove(); + $success = null; + }, 1000); } - function setSubmitEnabled(enabled) { + function setEnabled($el, enabled) { if (enabled) { - $submit.removeAttr('disabled').removeClass('disabled'); + $el.removeAttr('disabled').removeClass('disabled'); } else { - $submit.attr('disabled', 'disabled').addClass('disabled'); + $el.attr('disabled', 'disabled').addClass('disabled'); } } - function setValue(value) { - $input.val(value); + function setUrlValue(value) { + $urlInput.val(value); + } + + function setEnabledValue(value) { + $enabledInput.prop('checked', value); } $form.submit(function(event) { event.preventDefault(); addSpinner(); - setInputEnabled(false); - setSubmitEnabled(false); + setEnabled($urlInput, false); + setEnabled($enabledInput, false); + setEnabled($submit, false); server .rest({ url: resourceUrl('settings'), type: 'POST', data: { - url: $input.val() + url: $urlInput.val(), + enabled: $enabledInput.prop('checked') } }) .always(function() { removeSpinner(); - setInputEnabled(true); - setSubmitEnabled(true); + flashSuccess(); + setEnabled($urlInput, true); + setEnabled($enabledInput, true); + setEnabled($submit, true); }); }); - setInputEnabled(false); - setSubmitEnabled(false); + addSpinner(); + setEnabled($urlInput, false); + setEnabled($enabledInput, false); + setEnabled($submit, false); server .rest({ @@ -86,11 +98,14 @@ define('plugin/log-on-clone/repository-settings', [ }) .then(function(response) { if (response) { - setValue(response.url); + setUrlValue(response.url); + setEnabledValue(response.enabled); } - setInputEnabled(true); - setSubmitEnabled(true); + removeSpinner(); + setEnabled($urlInput, true); + setEnabled($enabledInput, true); + setEnabled($submit, true); }); } diff --git a/src/main/resources/static/clone-log-settings.soy b/src/main/resources/static/clone-log-settings.soy index 1abbe08..551b9c4 100644 --- a/src/main/resources/static/clone-log-settings.soy +++ b/src/main/resources/static/clone-log-settings.soy @@ -50,6 +50,22 @@ * Actual settings form */ {template .settingsContent} + {call aui.form.fieldGroup} + {param content} + {call aui.form.label} + {param forField: 'enabled' /} + {param content: getText('bitbucket.web.repository.log-on-clone.setting.enabled.label') /} + {/call} + {call aui.form.input} + {param id: 'enabled' /} + {param name: 'enabled' /} + {param type: 'checkbox' /} + {/call} + {call aui.form.fieldDescription} + {param text: getText('bitbucket.web.repository.log-on-clone.setting.enabled.description') /} + {/call} + {/param} + {/call} {call aui.form.fieldGroup} {param content} {call aui.form.label} @@ -64,6 +80,7 @@ {param placeholderText: getText('bitbucket.web.repository.log-on-clone.setting.url.placeholder') /} {param extraAttributes} pattern="https?://.*" + required {/param} {/call} {call aui.form.fieldDescription} From e36f98cdcd2c90d33d56beadbdbff4122d04114e Mon Sep 17 00:00:00 2001 From: David Recuenco Date: Sat, 21 Mar 2020 20:08:46 +0100 Subject: [PATCH 3/8] ADJUST formatting --- .editorconfig | 6 +- .gitignore | 2 +- .vscode/settings.json | 5 + pom.xml | 426 +++++++++--------- .../META-INF/spring/plugin-context.xml | 16 +- src/main/resources/atlassian-plugin.xml | 94 ++-- 6 files changed, 279 insertions(+), 270 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.editorconfig b/.editorconfig index bcafffd..899fa63 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,7 @@ -[*.java] +[{*.java,*.xml}] indent_style = tab indent_size = 4 + +[{*.css,*.js,*.soy}] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore index f706c22..bafd62c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .project .settings -.vscode +.classpath target tmp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f7527fc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic", + "maven.executable.path": "/usr/local/Cellar/atlassian-plugin-sdk/8.0.16/libexec/apache-maven-3.5.4/bin/mvn", + "java.configuration.maven.userSettings": "/usr/local/Cellar/atlassian-plugin-sdk/8.0.16/libexec/apache-maven-3.5.4/conf/settings.xml" +} diff --git a/pom.xml b/pom.xml index 94512c5..944209f 100644 --- a/pom.xml +++ b/pom.xml @@ -1,216 +1,216 @@ - 4.0.0 - - com.recuencojones.bitbucket - log-on-clone - 1.0.0-SNAPSHOT - atlassian-plugin - - log-on-clone - This is the com.recuencojones.bitbucket:log-on-clone plugin for Atlassian Bitbucket Server. - - - Example Company - http://www.example.com/ - - - - true - 1.8 - 1.8 - UTF-8 - - 8.0.2 - 5.16.0 - ${bitbucket.version} - - - ${project.groupId}.${project.artifactId} - - 2.1.7 - 1 - 1.1.1 - 2.0.1 - - - - - - com.atlassian.bitbucket.server - bitbucket-parent - ${bitbucket.version} - pom - import - - - - - - com.atlassian.bitbucket.server - bitbucket-api - provided - - - com.atlassian.bitbucket.server - bitbucket-spi - provided - - - com.atlassian.activeobjects - activeobjects-plugin - provided - - - com.atlassian.soy - soy-template-renderer-api - provided - - - com.atlassian.plugins - atlassian-plugins-webresource - provided - - - com.atlassian.plugins.rest - atlassian-rest-common - provided - - - com.atlassian.plugins.rest - atlassian-rest-module - provided - - - com.atlassian.bitbucket.server - bitbucket-rest-model - provided - - - - org.springframework - spring-beans - provided - - - org.springframework - spring-context - provided - - - - com.atlassian.plugin - atlassian-spring-scanner-annotation - ${atlassian.spring.scanner.version} - provided - - - com.atlassian.sal - sal-api - provided - - - - com.google.code.gson - gson - provided - - - javax.inject - javax.inject - ${javax.inject.version} - provided - - - javax.servlet - javax.servlet-api - provided - - - javax.ws.rs - jsr311-api - ${jsr311.version} - provided - - - org.apache.commons - commons-lang3 - provided - - - - com.atlassian.plugins - atlassian-plugins-osgi-testrunner - ${plugin.testrunner.version} - test - - - junit - junit - test - - - - - - - com.atlassian.maven.plugins - bitbucket-maven-plugin - ${amps.version} - true - - - - bitbucket - bitbucket - ${bitbucket.version} - ${bitbucket.data.version} - - - - ${atlassian.plugin.key} - - - - com.recuencojones.bitbucket.log, - com.recuencojones.bitbucket.log.dao, - com.recuencojones.bitbucket.log.api, - - - - - org.springframework.osgi.*;resolution:="optional", - org.eclipse.gemini.blueprint.*;resolution:="optional", - * - - - - * - - - - - com.atlassian.plugin - atlassian-spring-scanner-maven-plugin - ${atlassian.spring.scanner.version} - - - - atlassian-spring-scanner - - process-classes - - - - - - com.atlassian.plugin - atlassian-spring-scanner-external-jar - - - false - - - - + 4.0.0 + + com.recuencojones.bitbucket + log-on-clone + 1.0.0-SNAPSHOT + atlassian-plugin + + log-on-clone + This is the com.recuencojones.bitbucket:log-on-clone plugin for Atlassian Bitbucket Server. + + + Example Company + http://www.example.com/ + + + + true + 1.8 + 1.8 + UTF-8 + + 8.0.2 + 5.16.0 + ${bitbucket.version} + + + ${project.groupId}.${project.artifactId} + + 2.1.7 + 1 + 1.1.1 + 2.0.1 + + + + + + com.atlassian.bitbucket.server + bitbucket-parent + ${bitbucket.version} + pom + import + + + + + + com.atlassian.bitbucket.server + bitbucket-api + provided + + + com.atlassian.bitbucket.server + bitbucket-spi + provided + + + com.atlassian.activeobjects + activeobjects-plugin + provided + + + com.atlassian.soy + soy-template-renderer-api + provided + + + com.atlassian.plugins + atlassian-plugins-webresource + provided + + + com.atlassian.plugins.rest + atlassian-rest-common + provided + + + com.atlassian.plugins.rest + atlassian-rest-module + provided + + + com.atlassian.bitbucket.server + bitbucket-rest-model + provided + + + + org.springframework + spring-beans + provided + + + org.springframework + spring-context + provided + + + + com.atlassian.plugin + atlassian-spring-scanner-annotation + ${atlassian.spring.scanner.version} + provided + + + com.atlassian.sal + sal-api + provided + + + + com.google.code.gson + gson + provided + + + javax.inject + javax.inject + ${javax.inject.version} + provided + + + javax.servlet + javax.servlet-api + provided + + + javax.ws.rs + jsr311-api + ${jsr311.version} + provided + + + org.apache.commons + commons-lang3 + provided + + + + com.atlassian.plugins + atlassian-plugins-osgi-testrunner + ${plugin.testrunner.version} + test + + + junit + junit + test + + + + + + + com.atlassian.maven.plugins + bitbucket-maven-plugin + ${amps.version} + true + + + + bitbucket + bitbucket + ${bitbucket.version} + ${bitbucket.data.version} + + + + ${atlassian.plugin.key} + + + + com.recuencojones.bitbucket.log, + com.recuencojones.bitbucket.log.dao, + com.recuencojones.bitbucket.log.api, + + + + + org.springframework.osgi.*;resolution:="optional", + org.eclipse.gemini.blueprint.*;resolution:="optional", + * + + + + * + + + + + com.atlassian.plugin + atlassian-spring-scanner-maven-plugin + ${atlassian.spring.scanner.version} + + + + atlassian-spring-scanner + + process-classes + + + + + + com.atlassian.plugin + atlassian-spring-scanner-external-jar + + + false + + + + diff --git a/src/main/resources/META-INF/spring/plugin-context.xml b/src/main/resources/META-INF/spring/plugin-context.xml index b1f0519..0caa4e8 100644 --- a/src/main/resources/META-INF/spring/plugin-context.xml +++ b/src/main/resources/META-INF/spring/plugin-context.xml @@ -1,10 +1,10 @@ - - \ No newline at end of file + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:atlassian-scanner="http://www.atlassian.com/schema/atlassian-scanner" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-2.5.xsd + http://www.atlassian.com/schema/atlassian-scanner + http://www.atlassian.com/schema/atlassian-scanner/atlassian-scanner.xsd"> + + diff --git a/src/main/resources/atlassian-plugin.xml b/src/main/resources/atlassian-plugin.xml index 5d175c1..ee4d916 100644 --- a/src/main/resources/atlassian-plugin.xml +++ b/src/main/resources/atlassian-plugin.xml @@ -1,56 +1,56 @@ - - ${project.description} - ${project.version} - - true - images/pluginIcon.png - images/pluginLogo.png - + + ${project.description} + ${project.version} + + true + images/pluginIcon.png + images/pluginLogo.png + - - + + - - - Log clone events to a remote service - ${navBuilder.pluginServlets().path('log-on-clone', $project.key, $repository.slug).buildRelNoContext()} - + + + Log clone events to a remote service + ${navBuilder.pluginServlets().path('log-on-clone', $project.key, $repository.slug).buildRelNoContext()} + - - /log-on-clone/* - + + /log-on-clone/* + - - REQUEST - FORWARD - - extension.filter.excludes - .* - - com.recuencojones.bitbucket.log.rest - + + REQUEST + FORWARD + + extension.filter.excludes + .* + + com.recuencojones.bitbucket.log.rest + - - The module configuring the Active Objects service used by this plugin - com.recuencojones.bitbucket.log.dao.RepositoryCloneSettings - + + The module configuring the Active Objects service used by this plugin + com.recuencojones.bitbucket.log.dao.RepositoryCloneSettings + - - - /**/*.soy - - com.atlassian.bitbucket.server.bitbucket-web:server-soy-templates - + + + /**/*.soy + + com.atlassian.bitbucket.server.bitbucket-web:server-soy-templates + - - - /**/*.soy - - bitbucket.page.repository.settings.log-on-clone - com.atlassian.bitbucket.server.bitbucket-web-api:navbuilder - com.atlassian.bitbucket.server.bitbucket-web-api:server - com.atlassian.bitbucket.server.bitbucket-web:global - com.atlassian.auiplugin:aui-experimental-spinner - + + + /**/*.soy + + bitbucket.page.repository.settings.log-on-clone + com.atlassian.bitbucket.server.bitbucket-web-api:navbuilder + com.atlassian.bitbucket.server.bitbucket-web-api:server + com.atlassian.bitbucket.server.bitbucket-web:global + com.atlassian.auiplugin:aui-experimental-spinner + From fb4cf6434ae7f83fd81be7ef0c34ff40372889e3 Mon Sep 17 00:00:00 2001 From: David Recuenco Date: Sat, 21 Mar 2020 20:10:39 +0100 Subject: [PATCH 4/8] REMOVE unused imports and fields --- .../bitbucket/log/OnRepositoryClone.java | 13 +++---------- .../bitbucket/log/OnRepositoryDeleted.java | 5 ----- .../log/RepositoryCloneSettingsServlet.java | 12 +----------- .../log/dao/RepositoryCloneSettingsDAO.java | 3 --- .../log/rest/RepositoryCloneSettingsDTO.java | 2 -- .../log/rest/RepositoryCloneSettingsResource.java | 1 - 6 files changed, 4 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryClone.java b/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryClone.java index 85231ee..f8a851a 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryClone.java +++ b/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryClone.java @@ -5,22 +5,15 @@ import com.atlassian.bitbucket.event.repository.RepositoryCloneEvent; import com.atlassian.bitbucket.repository.Repository; import com.atlassian.event.api.EventListener; -import com.atlassian.event.api.EventPublisher; import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; import com.atlassian.sal.api.net.*; -import com.atlassian.sal.api.pluginsettings.PluginSettings; -import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; import com.google.gson.Gson; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; - import javax.inject.Inject; import javax.inject.Named; @@ -29,13 +22,13 @@ public class OnRepositoryClone { private static final Logger log = LoggerFactory.getLogger(OnRepositoryClone.class); @ComponentImport - private final RequestFactory requestFactory; + private final RequestFactory requestFactory; private final RepositoryCloneSettingsDAO repositoryCloneSettingsDAO; @Inject public OnRepositoryClone( - final RequestFactory requestFactory, + final RequestFactory requestFactory, final RepositoryCloneSettingsDAO repositoryCloneSettingsDAO ) { this.requestFactory = requestFactory; @@ -54,7 +47,7 @@ public void onCloneEvent(final RepositoryCloneEvent event) { if (settings != null && settings.isEnabled()) { log.debug("Repository {}/{} has log-on-clone settings", projectKey, repositorySlug); - final Request request = requestFactory.createRequest(Request.MethodType.POST, settings.getURL()); + final Request request = requestFactory.createRequest(Request.MethodType.POST, settings.getURL()); request.setRequestBody(new Gson().toJson(repository)); diff --git a/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryDeleted.java b/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryDeleted.java index d7b7864..910db8c 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryDeleted.java +++ b/src/main/java/com/recuencojones/bitbucket/log/OnRepositoryDeleted.java @@ -8,11 +8,6 @@ import com.atlassian.event.api.EventListener; -import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; - -import com.atlassian.sal.api.pluginsettings.PluginSettings; -import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/recuencojones/bitbucket/log/RepositoryCloneSettingsServlet.java b/src/main/java/com/recuencojones/bitbucket/log/RepositoryCloneSettingsServlet.java index 3afd62d..1042031 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/RepositoryCloneSettingsServlet.java +++ b/src/main/java/com/recuencojones/bitbucket/log/RepositoryCloneSettingsServlet.java @@ -5,9 +5,6 @@ import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; -import com.atlassian.sal.api.pluginsettings.PluginSettings; -import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; - import com.atlassian.soy.renderer.SoyException; import com.atlassian.soy.renderer.SoyTemplateRenderer; import com.atlassian.webresource.api.assembler.PageBuilderService; @@ -15,9 +12,6 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import javax.inject.Inject; import javax.servlet.ServletException; @@ -30,10 +24,8 @@ import java.util.Map; public class RepositoryCloneSettingsServlet extends HttpServlet { - private static final Logger log = LoggerFactory.getLogger(RepositoryCloneSettingsServlet.class); - @ComponentImport - private final PluginSettingsFactory pluginSettingsFactory; + private static final long serialVersionUID = 1L; @ComponentImport private final RepositoryService repositoryService; @@ -46,12 +38,10 @@ public class RepositoryCloneSettingsServlet extends HttpServlet { @Inject public RepositoryCloneSettingsServlet( - final PluginSettingsFactory pluginSettingsFactory, final RepositoryService repositoryService, final SoyTemplateRenderer soyTemplateRenderer, final PageBuilderService pageBuilderService ) { - this.pluginSettingsFactory = pluginSettingsFactory; this.repositoryService = repositoryService; this.soyTemplateRenderer = soyTemplateRenderer; this.pageBuilderService = pageBuilderService; diff --git a/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java index b92029e..5f10a3d 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java +++ b/src/main/java/com/recuencojones/bitbucket/log/dao/RepositoryCloneSettingsDAO.java @@ -5,9 +5,6 @@ import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; import org.springframework.stereotype.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import net.java.ao.DBParam; import javax.inject.Inject; diff --git a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java index a03080a..24ed133 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java +++ b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsDTO.java @@ -3,8 +3,6 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.annotate.JsonProperty; -import java.util.List; - @JsonIgnoreProperties(ignoreUnknown = true) public class RepositoryCloneSettingsDTO { diff --git a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java index f36d3d0..a03bbc7 100644 --- a/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java +++ b/src/main/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResource.java @@ -1,6 +1,5 @@ package com.recuencojones.bitbucket.log.rest; -import com.recuencojones.bitbucket.log.rest.*; import com.recuencojones.bitbucket.log.dao.*; import com.atlassian.bitbucket.repository.Repository; From 81b94fba355ac948977e27d98eaa458470e36d08 Mon Sep 17 00:00:00 2001 From: RecuencoJones Date: Sat, 21 Mar 2020 23:54:24 +0100 Subject: [PATCH 5/8] ADD travis --- .travis.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2325534 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +language: java +jdk: + - oraclejdk8 +before_install: + - wget https://marketplace.atlassian.com/download/plugins/atlassian-plugin-sdk-tgz + - mkdir opt + - tar -xvzf *plugin-sdk* -C opt + - mv opt/*plugin-sdk* opt/atlassian-plugin-sdk + - chmod a+x opt/atlassian-plugin-sdk/bin/* + - chmod a+x opt/atlassian-plugin-sdk/apache-maven-*/bin/* + - export PATH=opt/atlassian-plugin-sdk/bin:opt/atlassian-plugin-sdk/apache-maven-*/bin:$PATH + - atlas-version +install: + - atlas-mvn install +script: + - atlas-package From 45a85210e65d1d3cb1e8069f2efcafd88f569225 Mon Sep 17 00:00:00 2001 From: David Recuenco Date: Sun, 22 Mar 2020 00:08:31 +0100 Subject: [PATCH 6/8] Update .travis.yml --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2325534..b31ee74 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ +dist: trusty language: java + jdk: - - oraclejdk8 + - openjdk8 before_install: - wget https://marketplace.atlassian.com/download/plugins/atlassian-plugin-sdk-tgz - mkdir opt From a680d0c98b2ed2b2feb33ef6c96a58aea5b5b2ff Mon Sep 17 00:00:00 2001 From: David Recuenco Date: Mon, 23 Mar 2020 09:14:24 +0100 Subject: [PATCH 7/8] UPDATE readme, formatting --- .editorconfig | 4 +++- .travis.yml | 1 - README => README.md | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) rename README => README.md (88%) diff --git a/.editorconfig b/.editorconfig index 899fa63..ef924dc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,9 @@ +root = true + [{*.java,*.xml}] indent_style = tab indent_size = 4 -[{*.css,*.js,*.soy}] +[{*.css,*.js,*.soy,*.yml}] indent_style = space indent_size = 2 diff --git a/.travis.yml b/.travis.yml index b31ee74..a5e17fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ dist: trusty language: java - jdk: - openjdk8 before_install: diff --git a/README b/README.md similarity index 88% rename from README rename to README.md index 1dbfe2f..f948985 100644 --- a/README +++ b/README.md @@ -23,4 +23,7 @@ Useful resources: Code samples: +* https://bitbucket.org/atlassian/stash-git-ops-plugin/src/master/ +* https://bitbucket.org/atlassianlabs/stash-refchange-settings-plugin/src/master/ * https://bitbucket.org/atlassian/bitbucket-code-coverage/src/master/code-coverage-plugin/ +* https://bitbucket.org/atlassian/stash-auto-unapprove-plugin/src/master/ From 3c8e499282cef2a1725233a91d05bc6305284f20 Mon Sep 17 00:00:00 2001 From: David Recuenco Date: Mon, 23 Mar 2020 17:14:50 +0100 Subject: [PATCH 8/8] ADD unit tests --- .travis.yml | 2 +- pom.xml | 5 + .../bitbucket/log/OnRepositoryCloneTest.java | 88 +++++++++++++++++ .../log/OnRepositoryDeletedTest.java | 64 ++++++++++++ .../RepositoryCloneSettingsResourceTest.java | 97 +++++++++++++++++++ 5 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/recuencojones/bitbucket/log/OnRepositoryCloneTest.java create mode 100644 src/test/java/com/recuencojones/bitbucket/log/OnRepositoryDeletedTest.java create mode 100644 src/test/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResourceTest.java diff --git a/.travis.yml b/.travis.yml index a5e17fe..de456f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,6 @@ before_install: - export PATH=opt/atlassian-plugin-sdk/bin:opt/atlassian-plugin-sdk/apache-maven-*/bin:$PATH - atlas-version install: - - atlas-mvn install + - atlas-mvn install -DskipTests=true script: - atlas-package diff --git a/pom.xml b/pom.xml index 944209f..bf8992e 100644 --- a/pom.xml +++ b/pom.xml @@ -149,6 +149,11 @@ junit test
+ + org.mockito + mockito-core + test + diff --git a/src/test/java/com/recuencojones/bitbucket/log/OnRepositoryCloneTest.java b/src/test/java/com/recuencojones/bitbucket/log/OnRepositoryCloneTest.java new file mode 100644 index 0000000..ef5e271 --- /dev/null +++ b/src/test/java/com/recuencojones/bitbucket/log/OnRepositoryCloneTest.java @@ -0,0 +1,88 @@ +package com.recuencojones.bitbucket.log; + +import com.atlassian.bitbucket.event.repository.RepositoryCloneEvent; +import com.atlassian.bitbucket.project.Project; +import com.atlassian.bitbucket.repository.Repository; +import com.atlassian.sal.api.net.Request; +import com.atlassian.sal.api.net.RequestFactory; +import com.atlassian.sal.api.net.ResponseException; +import com.recuencojones.bitbucket.log.dao.RepositoryCloneSettings; +import com.recuencojones.bitbucket.log.dao.RepositoryCloneSettingsDAO; + +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class OnRepositoryCloneTest { + final String url = "https://url"; + final String projectKey = "proj_1"; + final String repositorySlug = "repo_1"; + final int repositoryID = 1337; + + private Request mockRequest; + private RequestFactory mockRequestFactory; + private RepositoryCloneSettings mockRepositoryCloneSettings; + private RepositoryCloneSettingsDAO mockRepositoryCloneSettingsDAO; + private RepositoryCloneEvent mockEvent; + private Repository mockRepository; + private Project mockProject; + + @Before + public void setup() { + mockRequest = mock(Request.class); + mockRequestFactory = mock(RequestFactory.class); + mockRepositoryCloneSettings = mock(RepositoryCloneSettings.class); + mockRepositoryCloneSettingsDAO = mock(RepositoryCloneSettingsDAO.class); + mockEvent = mock(RepositoryCloneEvent.class); + mockRepository = mock(Repository.class); + mockProject = mock(Project.class); + + when(mockProject.getKey()).thenReturn(projectKey); + when(mockRepository.getId()).thenReturn(repositoryID); + when(mockRepository.getSlug()).thenReturn(repositorySlug); + when(mockRepository.getProject()).thenReturn(mockProject); + when(mockEvent.getRepository()).thenReturn(mockRepository); + when(mockRequestFactory.createRequest(Request.MethodType.POST, url)).thenReturn(mockRequest); + } + + @Test + public void testCloneRepositoryWithSettingsShouldExecutePostRequestWhenEnabled() throws ResponseException { + final OnRepositoryClone component = new OnRepositoryClone(mockRequestFactory, mockRepositoryCloneSettingsDAO); + + when(mockRepositoryCloneSettingsDAO.get(repositoryID)).thenReturn(mockRepositoryCloneSettings); + when(mockRepositoryCloneSettings.getURL()).thenReturn(url); + when(mockRepositoryCloneSettings.isEnabled()).thenReturn(true); + + component.onCloneEvent(mockEvent); + + verify(mockRequest).execute(); + } + + @Test + public void testCloneRepositoryWithSettingsShouldNotExecutePostRequestWhenNotEnabled() throws ResponseException { + final OnRepositoryClone component = new OnRepositoryClone(mockRequestFactory, mockRepositoryCloneSettingsDAO); + + when(mockRepositoryCloneSettingsDAO.get(repositoryID)).thenReturn(mockRepositoryCloneSettings); + when(mockRepositoryCloneSettings.getURL()).thenReturn(url); + when(mockRepositoryCloneSettings.isEnabled()).thenReturn(false); + + component.onCloneEvent(mockEvent); + + verify(mockRequest, never()).execute(); + } + + @Test + public void testCloneRepositoryWithoutSettingsShouldNotExecutePostRequest() throws ResponseException { + final OnRepositoryClone component = new OnRepositoryClone(mockRequestFactory, mockRepositoryCloneSettingsDAO); + + when(mockRepositoryCloneSettingsDAO.get(repositoryID)).thenReturn(null); + + component.onCloneEvent(mockEvent); + + verify(mockRequest, never()).execute(); + } +} diff --git a/src/test/java/com/recuencojones/bitbucket/log/OnRepositoryDeletedTest.java b/src/test/java/com/recuencojones/bitbucket/log/OnRepositoryDeletedTest.java new file mode 100644 index 0000000..3b90213 --- /dev/null +++ b/src/test/java/com/recuencojones/bitbucket/log/OnRepositoryDeletedTest.java @@ -0,0 +1,64 @@ +package com.recuencojones.bitbucket.log; + +import com.atlassian.bitbucket.event.repository.RepositoryDeletedEvent; +import com.atlassian.bitbucket.project.Project; +import com.atlassian.bitbucket.repository.Repository; +import com.recuencojones.bitbucket.log.dao.RepositoryCloneSettings; +import com.recuencojones.bitbucket.log.dao.RepositoryCloneSettingsDAO; + +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class OnRepositoryDeletedTest { + final String projectKey = "proj_1"; + final String repositorySlug = "repo_1"; + final int repositoryID = 1337; + + private RepositoryCloneSettings mockRepositoryCloneSettings; + private RepositoryCloneSettingsDAO mockRepositoryCloneSettingsDAO; + private RepositoryDeletedEvent mockEvent; + private Repository mockRepository; + private Project mockProject; + + @Before + public void setup() { + mockRepositoryCloneSettings = mock(RepositoryCloneSettings.class); + mockRepositoryCloneSettingsDAO = mock(RepositoryCloneSettingsDAO.class); + mockEvent = mock(RepositoryDeletedEvent.class); + mockRepository = mock(Repository.class); + mockProject = mock(Project.class); + + when(mockProject.getKey()).thenReturn(projectKey); + when(mockRepository.getId()).thenReturn(repositoryID); + when(mockRepository.getSlug()).thenReturn(repositorySlug); + when(mockRepository.getProject()).thenReturn(mockProject); + when(mockEvent.getRepository()).thenReturn(mockRepository); + } + + @Test + public void testDeleteRepositoryWithSettingsShouldRemoveSettings() { + final OnRepositoryDeleted component = new OnRepositoryDeleted(mockRepositoryCloneSettingsDAO); + + when(mockRepositoryCloneSettingsDAO.get(repositoryID)).thenReturn(mockRepositoryCloneSettings); + + component.onRepositoryDeleted(mockEvent); + + verify(mockRepositoryCloneSettingsDAO).remove(mockRepositoryCloneSettings); + } + + @Test + public void testDeleteRepositoryWithoutSettingsShouldNotRemoveSettings() { + final OnRepositoryDeleted component = new OnRepositoryDeleted(mockRepositoryCloneSettingsDAO); + + when(mockRepositoryCloneSettingsDAO.get(repositoryID)).thenReturn(null); + + component.onRepositoryDeleted(mockEvent); + + verify(mockRepositoryCloneSettingsDAO, never()).remove(mockRepositoryCloneSettings); + } +} diff --git a/src/test/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResourceTest.java b/src/test/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResourceTest.java new file mode 100644 index 0000000..a7f6089 --- /dev/null +++ b/src/test/java/com/recuencojones/bitbucket/log/rest/RepositoryCloneSettingsResourceTest.java @@ -0,0 +1,97 @@ +package com.recuencojones.bitbucket.log.rest; + +import com.atlassian.bitbucket.AuthorisationException; +import com.atlassian.bitbucket.permission.Permission; +import com.atlassian.bitbucket.permission.PermissionValidationService; +import com.atlassian.bitbucket.project.Project; +import com.atlassian.bitbucket.repository.Repository; +import com.recuencojones.bitbucket.log.dao.RepositoryCloneSettings; +import com.recuencojones.bitbucket.log.dao.RepositoryCloneSettingsDAO; + +import javax.ws.rs.core.Response; + +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class RepositoryCloneSettingsResourceTest { + final boolean enabled = true; + final String url = "https://url"; + final String projectKey = "proj_1"; + final String repositorySlug = "repo_1"; + final int repositoryID = 1337; + + private PermissionValidationService mockPermissionValidationService; + private RepositoryCloneSettings mockRepositoryCloneSettings; + private RepositoryCloneSettingsDAO mockRepositoryCloneSettingsDAO; + private Repository mockRepository; + private Project mockProject; + + @Before + public void setup() { + mockPermissionValidationService = mock(PermissionValidationService.class); + mockRepositoryCloneSettings = mock(RepositoryCloneSettings.class); + mockRepositoryCloneSettingsDAO = mock(RepositoryCloneSettingsDAO.class); + mockRepository = mock(Repository.class); + mockProject = mock(Project.class); + + when(mockProject.getKey()).thenReturn(projectKey); + when(mockRepository.getId()).thenReturn(repositoryID); + when(mockRepository.getSlug()).thenReturn(repositorySlug); + when(mockRepository.getProject()).thenReturn(mockProject); + } + + @Test + public void testGetSettingsForRepositoryWithConfigShouldReturnSettingsObject() { + RepositoryCloneSettingsResource component = new RepositoryCloneSettingsResource(mockPermissionValidationService, mockRepositoryCloneSettingsDAO); + + when(mockRepositoryCloneSettingsDAO.get(repositoryID)).thenReturn(mockRepositoryCloneSettings); + + Response res = component.getSettings(mockRepository); + + assertThat(res.getEntity(), instanceOf(RepositoryCloneSettingsDTO.class)); + } + + @Test + public void testGetSettingsForRepositoryWithoutConfigShouldReturnNull() { + RepositoryCloneSettingsResource component = new RepositoryCloneSettingsResource(mockPermissionValidationService, mockRepositoryCloneSettingsDAO); + + when(mockRepositoryCloneSettingsDAO.get(repositoryID)).thenReturn(null); + + Response res = component.getSettings(mockRepository); + + assertNull(res.getEntity()); + } + + @Test + public void testSaveSettingsForRepositoryAdminShouldSucceed() { + RepositoryCloneSettingsResource component = new RepositoryCloneSettingsResource(mockPermissionValidationService, mockRepositoryCloneSettingsDAO); + RepositoryCloneSettingsDTO settings = new RepositoryCloneSettingsDTO(); + + settings.setEnabled(enabled); + settings.setURL(url); + + component.saveSettings(mockRepository, settings); + + verify(mockRepositoryCloneSettingsDAO).save(repositoryID, url, enabled); + } + + @Test(expected = AuthorisationException.class) + public void testSaveSettingsForNonRepositoryAdminShouldFail() { + RepositoryCloneSettingsResource component = new RepositoryCloneSettingsResource(mockPermissionValidationService, mockRepositoryCloneSettingsDAO); + RepositoryCloneSettingsDTO settings = new RepositoryCloneSettingsDTO(); + + doThrow(AuthorisationException.class) + .when(mockPermissionValidationService) + .validateForRepository(mockRepository, Permission.REPO_ADMIN); + + component.saveSettings(mockRepository, settings); + } +}