From 9531b4dc8c2fb856f6e9d2f91c45fdbb190fb693 Mon Sep 17 00:00:00 2001 From: Patrick Huang Date: Mon, 19 Sep 2016 15:51:00 +1000 Subject: [PATCH 1/6] ZNTA-1290 - manually triggered webhook --- .../main/java/org/zanata/model/WebHook.java | 16 +++-- .../org/zanata/model/type/WebhookType.java | 4 +- .../java/org/zanata/action/ProjectHome.java | 10 +-- .../org/zanata/action/VersionHomeAction.java | 39 ++++++++++ .../main/java/org/zanata/dao/WebHookDAO.java | 41 +++++++++++ .../org/zanata/service/ProjectService.java | 5 +- .../service/impl/ProjectServiceImpl.java | 10 +-- .../service/impl/WebhookServiceImpl.java | 24 ++++++- .../events/ManuallyTriggeredEvent.java | 72 +++++++++++++++++++ .../db/changelogs/db.changelog-4.0.xml | 7 ++ .../src/main/resources/messages.properties | 5 ++ .../layout/project/settings-tab-webhook.xhtml | 6 +- .../layout/version/languages-tab.xhtml | 13 ++++ .../resources/script/components-script.js | 6 +- .../resources/zanata/webbook-form.xhtml | 12 +++- .../FilterConstraintToQueryJpaTest.java | 2 +- .../service/impl/DocumentServiceImplTest.java | 6 +- .../impl/TranslationUpdatedManagerTest.java | 4 +- 18 files changed, 251 insertions(+), 31 deletions(-) create mode 100644 zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java diff --git a/zanata-model/src/main/java/org/zanata/model/WebHook.java b/zanata-model/src/main/java/org/zanata/model/WebHook.java index 486c2b72b5..18f4e5cc74 100644 --- a/zanata-model/src/main/java/org/zanata/model/WebHook.java +++ b/zanata-model/src/main/java/org/zanata/model/WebHook.java @@ -77,10 +77,16 @@ public class WebHook implements Serializable { @Column(nullable = true) private String secret; - public WebHook(HProject project, String url, Set types, - String secret) { + @Size(max = 255) + private String name; + + + public WebHook(HProject project, String url, String name, + Set types, + String secret) { this.project = project; this.url = url; + this.name = name; this.types = types; this.secret = secret; } @@ -109,14 +115,16 @@ public Set getTypes() { /** * This will replace all properties with given ones. - * * @param url - new url + * @param name - new name * @param newTypes - new types * @param secret - new secret key */ @Transient - public void update(String url, Set newTypes, String secret) { + public void update(String url, String name, Set newTypes, + String secret) { this.url = url; + this.name = name; this.secret = secret; /** diff --git a/zanata-model/src/main/java/org/zanata/model/type/WebhookType.java b/zanata-model/src/main/java/org/zanata/model/type/WebhookType.java index 910b7d1ec8..b86a93e199 100644 --- a/zanata-model/src/main/java/org/zanata/model/type/WebhookType.java +++ b/zanata-model/src/main/java/org/zanata/model/type/WebhookType.java @@ -14,7 +14,9 @@ public enum WebhookType implements Serializable { DocumentStatsEvent("Translation update"), VersionChangedEvent("Project version"), ProjectMaintainerChangedEvent("Project maintainer update"), - SourceDocumentChangedEvent("Document"); + SourceDocumentChangedEvent("Document"), + ManuallyTriggeredEvent("Manual event") + ; @Getter private String displayName; diff --git a/zanata-war/src/main/java/org/zanata/action/ProjectHome.java b/zanata-war/src/main/java/org/zanata/action/ProjectHome.java index 5f199a35a9..223a66863c 100644 --- a/zanata-war/src/main/java/org/zanata/action/ProjectHome.java +++ b/zanata-war/src/main/java/org/zanata/action/ProjectHome.java @@ -1087,7 +1087,7 @@ public List getValidationList() { } @Transactional - public void addWebHook(String url, String secret, String strTypes) { + public void addWebHook(String url, String secret, String strTypes, String name) { identity.checkPermission(getInstance(), "update"); Set types = getTypesFromString(strTypes); if(types.isEmpty()) { @@ -1103,7 +1103,7 @@ public void addWebHook(String url, String secret, String strTypes) { return; } boolean isAdded = projectServiceImpl.addWebhook(getInstance(), url, - secret, types); + secret, name, types); if (isAdded) { facesMessages.addGlobal( msgs.format("jsf.project.AddNewWebhook", url)); @@ -1114,8 +1114,8 @@ public void addWebHook(String url, String secret, String strTypes) { public void removeWebHook(String id) { identity.checkPermission(getInstance(), "update"); WebHook webHook = webHookDAO.findById(new Long(id)); - String url = webHook.getUrl(); if (webHook != null) { + String url = webHook.getUrl(); getInstance().getWebHooks().remove(webHook); webHookDAO.makeTransient(webHook); facesMessages.addGlobal( @@ -1125,7 +1125,7 @@ public void removeWebHook(String id) { @Transactional public void updateWebhook(String id, String url, String secret, - String strTypes) { + String strTypes, String name) { identity.checkPermission(getInstance(), "update"); Set types = getTypesFromString(strTypes); if(types.isEmpty()) { @@ -1143,7 +1143,7 @@ public void updateWebhook(String id, String url, String secret, return; } boolean updated = projectServiceImpl.updateWebhook(getInstance(), - webhookId, url, secret, types); + webhookId, url, secret, name, types); if (updated) { facesMessages.addGlobal( msgs.format("jsf.project.UpdateWebhook", url)); diff --git a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java index ed9ad0b94f..04011410d7 100644 --- a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java +++ b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import javax.enterprise.inject.Model; import javax.faces.application.FacesMessage; import javax.faces.bean.ViewScoped; @@ -47,6 +48,7 @@ import org.apache.deltaspike.jpa.api.transaction.Transactional; import org.richfaces.event.FileUploadEvent; import org.richfaces.model.UploadedFile; +import org.zanata.dao.WebHookDAO; import org.zanata.events.DocumentLocaleKey; import org.zanata.exception.AuthorizationException; import org.zanata.async.handle.CopyVersionTaskHandle; @@ -68,7 +70,9 @@ import org.zanata.model.HLocale; import org.zanata.model.HProjectIteration; import org.zanata.model.HRawDocument; +import org.zanata.model.WebHook; import org.zanata.model.type.TranslationSourceType; +import org.zanata.model.type.WebhookType; import org.zanata.rest.StringSet; import org.zanata.rest.dto.extensions.ExtensionType; import org.zanata.rest.dto.extensions.comment.SimpleComment; @@ -83,6 +87,7 @@ import org.zanata.service.TranslationService; import org.zanata.service.TranslationStateCache; import org.zanata.service.VersionStateCache; +import org.zanata.service.impl.WebhookServiceImpl; import org.zanata.ui.AbstractListFilter; import org.zanata.ui.AbstractSortAction; import org.zanata.ui.CopyAction; @@ -98,6 +103,7 @@ import org.zanata.webtrans.shared.model.DocumentStatus; import org.zanata.webtrans.shared.util.TokenUtil; +import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.collect.Lists; @@ -189,6 +195,13 @@ public class VersionHomeAction extends AbstractSortAction implements @Inject private UrlUtil urlUtil; + @Inject + private WebhookServiceImpl webhookService; + + @Inject + private WebHookDAO webHookDAO; + + private List supportedLocale; private List documents; @@ -326,6 +339,32 @@ public void cancelCopyVersion() { msgs.format("jsf.copyVersion.Cancelled", versionSlug)); } + public boolean canTriggerManualWebhook() { + boolean hasLocalePermission = + isUserAllowedToTranslateOrReview(selectedLocale); + List webHooks = webHookDAO.getWebHooksForType(projectSlug, + WebhookType.ManuallyTriggeredEvent); + return hasLocalePermission && !webHooks.isEmpty(); + } + + public void triggerManualWebhookEvent() { + List webHooks = webHookDAO.getWebHooksForType(projectSlug, + WebhookType.ManuallyTriggeredEvent); + + if (selectedLocale != null && !webHooks.isEmpty()) { + webhookService.processTranslationUpdated(projectSlug, versionSlug, + selectedLocale.getLocaleId(), webHooks); + } + } + + public String getManualWebhookNames() { + List webHooks = webHookDAO.getWebHooksForType(projectSlug, + WebhookType.ManuallyTriggeredEvent); + Set webhookNames = webHooks.stream().map(WebHook::getName).collect( + Collectors.toSet()); + return Joiner.on(", ").skipNulls().join(webhookNames); + } + // TODO Serializable only because it's a dependent bean @NoArgsConstructor public static class CopyVersionHandler extends CopyAction implements Serializable { diff --git a/zanata-war/src/main/java/org/zanata/dao/WebHookDAO.java b/zanata-war/src/main/java/org/zanata/dao/WebHookDAO.java index ab0e5659a7..3a4eefe721 100644 --- a/zanata-war/src/main/java/org/zanata/dao/WebHookDAO.java +++ b/zanata-war/src/main/java/org/zanata/dao/WebHookDAO.java @@ -1,10 +1,34 @@ +/* + * Copyright 2016, Red Hat, Inc. and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ package org.zanata.dao; +import java.util.List; + import org.hibernate.Session; import javax.enterprise.context.RequestScoped; import javax.inject.Named; + import org.zanata.model.WebHook; +import org.zanata.model.type.WebhookType; @Named("webHookDAO") @RequestScoped @@ -17,4 +41,21 @@ public WebHookDAO() { public WebHookDAO(Session session) { super(WebHook.class, session); } + + /** + * Get a list of webhook from a project which has the given type set up. + * @param projectSlug project slug + * @param type webhoot type + */ + public List getWebHooksForType(String projectSlug, + WebhookType type) { + // This generates a warning in log but as per https://hibernate.atlassian.net/browse/HHH-10621 + // the warning message is a hibernate issue + List list = getSession().createQuery( + "from WebHook w where w.project.slug = :projectSlug and :webhookType in elements(w.types) ") + .setParameter("projectSlug", projectSlug) + .setParameter("webhookType", type) + .setCacheable(true).setComment("getWebHooksForType").list(); + return list; + } } diff --git a/zanata-war/src/main/java/org/zanata/service/ProjectService.java b/zanata-war/src/main/java/org/zanata/service/ProjectService.java index 2a91fb9b73..0c2f355685 100644 --- a/zanata-war/src/main/java/org/zanata/service/ProjectService.java +++ b/zanata-war/src/main/java/org/zanata/service/ProjectService.java @@ -28,7 +28,6 @@ import org.zanata.service.impl.ProjectServiceImpl; import java.util.List; -import java.util.Optional; import java.util.Set; /** @@ -47,11 +46,11 @@ List updateProjectPermissions(HProject project, @Transactional boolean updateWebhook(HProject project, Long webhookId, String url, - String secret, Set types); + String secret, String name, Set types); @Transactional boolean addWebhook(HProject project, String url, String secret, - Set types); + String name, Set types); /** * Check if project contains duplicate webhook with matching url diff --git a/zanata-war/src/main/java/org/zanata/service/impl/ProjectServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/ProjectServiceImpl.java index 02224ca81f..3a3618d3ce 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/ProjectServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/ProjectServiceImpl.java @@ -21,6 +21,7 @@ package org.zanata.service.impl; +import com.google.common.base.Strings; import com.google.common.collect.Lists; import lombok.AllArgsConstructor; import lombok.Getter; @@ -49,7 +50,6 @@ import java.util.Optional; import java.util.Set; -import static javax.faces.application.FacesMessage.SEVERITY_ERROR; import static org.zanata.model.LocaleRole.Coordinator; import static org.zanata.model.LocaleRole.Reviewer; import static org.zanata.model.LocaleRole.Translator; @@ -109,7 +109,7 @@ public List updateProjectPermissions(HProject project, @Transactional @Override public boolean updateWebhook(HProject project, Long webhookId, String url, - String secret, Set types) { + String secret, String name, Set types) { if (types.isEmpty()) { return false; } @@ -121,7 +121,7 @@ public boolean updateWebhook(HProject project, Long webhookId, String url, return false; } secret = StringUtils.isBlank(secret) ? null : secret; - webHook.update(url, types, secret); + webHook.update(url, Strings.emptyToNull(name), types, secret); webHookDAO.makePersistent(webHook); return true; } @@ -129,7 +129,7 @@ public boolean updateWebhook(HProject project, Long webhookId, String url, @Transactional @Override public boolean addWebhook(HProject project, String url, String secret, - Set types) { + String name, Set types) { if (types.isEmpty()) { return false; } @@ -138,7 +138,7 @@ public boolean addWebhook(HProject project, String url, String secret, } secret = StringUtils.isBlank(secret) ? null : secret; WebHook webHook = - new WebHook(project, url, types, secret); + new WebHook(project, url, Strings.emptyToNull(name), types, secret); project.getWebHooks().add(webHook); projectDAO.makePersistent(project); return true; diff --git a/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java index e01195d4d8..14e4ed71a3 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java @@ -22,15 +22,18 @@ import org.zanata.events.WebhookEvent; import org.zanata.events.WebhookEventType; import org.zanata.i18n.Messages; +import org.zanata.model.HAccount; import org.zanata.model.ProjectRole; import org.zanata.model.WebHook; import org.zanata.model.type.WebhookType; +import org.zanata.security.annotations.Authenticated; import org.zanata.util.UrlUtil; import org.zanata.webhook.events.DocumentMilestoneEvent; import org.zanata.webhook.events.DocumentStatsEvent; import org.zanata.webhook.events.ProjectMaintainerChangedEvent; import org.zanata.webhook.events.SourceDocumentChangedEvent; import org.zanata.webhook.events.TestEvent; +import org.zanata.webhook.events.ManuallyTriggeredEvent; import org.zanata.webhook.events.VersionChangedEvent; import com.google.common.base.Function; @@ -52,6 +55,10 @@ public class WebhookServiceImpl implements Serializable { @Inject private Event webhookEventEvent; + @Inject + @Authenticated + private HAccount authenticatedUser; + private static final int URL_MAX_LENGTH = 255; /** @@ -152,6 +159,17 @@ public void processDocumentStats(String username, String projectSlug, publishWebhooks(webHooks, statsEvent); } + /** + * Process TranslationChangedEvent + */ + public void processTranslationUpdated(String projectSlug, + String versionSlug, LocaleId localeId, List webHooks) { + ManuallyTriggeredEvent event = + new ManuallyTriggeredEvent(authenticatedUser.getUsername(), + projectSlug, versionSlug, localeId); + publishWebhooks(webHooks, event); + } + public List getAvailableWebhookTypes() { WebhookTypeItem docMilestone = new WebhookTypeItem(WebhookType.DocumentMilestoneEvent, @@ -174,8 +192,12 @@ public List getAvailableWebhookTypes() { new WebhookTypeItem(WebhookType.SourceDocumentChangedEvent, msgs.get("jsf.webhookType.SourceDocumentChangedEvent.desc")); + WebhookTypeItem transUpdate = + new WebhookTypeItem(WebhookType.ManuallyTriggeredEvent, + msgs.get( + "jsf.webhookType.ManuallyTriggeredEvent.desc")); return Lists - .newArrayList(docMilestone, stats, version, maintainer, srcDoc); + .newArrayList(docMilestone, stats, version, maintainer, srcDoc, transUpdate); } public List getDisplayNames(Set types) { diff --git a/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java b/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java new file mode 100644 index 0000000000..d973e147f5 --- /dev/null +++ b/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java @@ -0,0 +1,72 @@ +/* + * Copyright 2016, Red Hat, Inc. and individual contributors as indicated by the + * @author tags. See the copyright.txt file in the distribution for a full + * listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 2.1 of the License, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this software; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF + * site: http://www.fsf.org. + */ + +package org.zanata.webhook.events; + +import javax.annotation.Nullable; + +import org.zanata.common.LocaleId; +import org.zanata.events.WebhookEventType; +import org.zanata.model.type.WebhookType; + +/** + * Indicates an event triggered by a user. + * + * @author Patrick Huang pahuang@redhat.com + */ +public class ManuallyTriggeredEvent extends WebhookEventType { + private static final String EVENT_TYPE = + WebhookType.ManuallyTriggeredEvent.name(); + + private String username; + private String project; + private String version; + private LocaleId locale; + + public @Nullable String getProject() { + return project; + } + + public @Nullable String getVersion() { + return version; + } + + public @Nullable LocaleId getLocale() { + return locale; + } + + public String getUsername() { + return username; + } + + public ManuallyTriggeredEvent(String triggeredBy, String project, String version, + LocaleId locale) { + this.username = triggeredBy; + this.project = project; + this.version = version; + this.locale = locale; + } + + @Override + public String getType() { + return EVENT_TYPE; + } +} diff --git a/zanata-war/src/main/resources/db/changelogs/db.changelog-4.0.xml b/zanata-war/src/main/resources/db/changelogs/db.changelog-4.0.xml index fa589ca050..62da719638 100644 --- a/zanata-war/src/main/resources/db/changelogs/db.changelog-4.0.xml +++ b/zanata-war/src/main/resources/db/changelogs/db.changelog-4.0.xml @@ -191,4 +191,11 @@ constraintName="UKWebHookUrl_projectId" /> + + Add name column to webhook + + + + + diff --git a/zanata-war/src/main/resources/messages.properties b/zanata-war/src/main/resources/messages.properties index 2ae8c57f1e..0d3c5d32e2 100644 --- a/zanata-war/src/main/resources/messages.properties +++ b/zanata-war/src/main/resources/messages.properties @@ -325,6 +325,7 @@ jsf.project.AddNewWebhook=Webhook {0} added. jsf.project.webhookType.empty=Please select a type for this webhook. jsf.project.UpdateWebhook=Webhook {0} updated. jsf.project.PayloadURL=Payload URL +jsf.project.WebhookName=Webhook name jsf.project.WebhookType.label=Type jsf.project.InvalidUrl=Invalid URL: {0} jsf.project.DuplicateUrl=Same URL is already in the list. {0} @@ -336,6 +337,7 @@ jsf.webhookType.DocumentStatsEvent.desc=Translations are updated (singly or in a jsf.webhookType.VersionChangedEvent.desc=Project version is created or removed jsf.webhookType.ProjectMaintainerChangedEvent.desc=Project maintainer is added or removed jsf.webhookType.SourceDocumentChangedEvent.desc=Source document is added or removed +jsf.webhookType.ManuallyTriggeredEvent.desc=A Event that can be triggered manually #------ [home] > Projects > [project-id] ------ jsf.ReadOnlyVersions=Read-only versions @@ -648,6 +650,9 @@ jsf.pager.PreviousPage=Previous jsf.iteration.ExportTMXIter=Export Version to TMX jsf.iteration.ExportTMX.Language=Export {0} Documents to TMX jsf.iteration.files.NoFiles=No Files Available + +jsf.iteration.triggerWebHook=Trigger {0} webhook for {1} + ! local directory path where a document is located within the project jsf.iteration.files.Path=Path jsf.iteration.files.Filter.title=Filter by document name diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml index f53682ef11..0b2f1d10bc 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml @@ -16,12 +16,13 @@ + + + +
@@ -226,6 +229,16 @@ + + +
  • + + #{msgs.format('jsf.iteration.triggerWebHook', versionHomeAction.manualWebhookNames, versionHomeAction.selectedLocale.localeId)} + + +
  • +
    + diff --git a/zanata-war/src/main/webapp/resources/script/components-script.js b/zanata-war/src/main/webapp/resources/script/components-script.js index eaae5496d8..f00474660f 100644 --- a/zanata-war/src/main/webapp/resources/script/components-script.js +++ b/zanata-war/src/main/webapp/resources/script/components-script.js @@ -497,7 +497,8 @@ function onAddWebhook(formId, callback) { var url = form.find('[name="payloadUrlInput"]').val(); var secret = form.find('[name="secretInput"]').val(); var types = form.find('[name="webhookTypes"]').val(); - callback(url, secret, types, formId); + var name = form.find('[name="nameInput"]').val(); + callback(url, secret, types, formId, name); } function onUpdateWebhook(id, formId, callback) { @@ -505,5 +506,6 @@ function onUpdateWebhook(id, formId, callback) { var url = form.find('[name="payloadUrlInput"]').val(); var secret = form.find('[name="secretInput"]').val(); var types = form.find('[name="webhookTypes"]').val(); - callback(id, url, secret, types, formId); + var name = form.find('[name="nameInput"]').val(); + callback(id, url, secret, types, formId, name); } diff --git a/zanata-war/src/main/webapp/resources/zanata/webbook-form.xhtml b/zanata-war/src/main/webapp/resources/zanata/webbook-form.xhtml index 22735e3937..abad2e8b4d 100644 --- a/zanata-war/src/main/webapp/resources/zanata/webbook-form.xhtml +++ b/zanata-war/src/main/webapp/resources/zanata/webbook-form.xhtml @@ -16,10 +16,10 @@ shortDescription="Require if webhook is null. js handler for test webhook. Fn(url, secret, webhookTypes, formId)" /> + shortDescription="Require if webhook is null. js handler for add webhook. Fn(url, secret, webhookTypes, formId, name)"/> + shortDescription="Require if webhook is not null. js handler for update webhook. Fn(id, url, secret, webhookTypes, formId, name)"/> @@ -68,6 +68,14 @@ placeholder="#{msgs['jsf.project.PayloadURL']}" class="form--inline__input" value="#{cc.attrs.webhook.url}"/>
    +
    + + +
    types = Sets.newHashSet(WebhookType.DocumentMilestoneEvent); webHooks.add(new WebHook(project, "http://test.example.com", - types, key)); + "test", types, key)); webHooks.add(new WebHook(project, "http://test1.example.com", - types, key)); + "test1", types, key)); webHooks.add(new WebHook(project, "http://test1.example.com", - Sets.newHashSet(WebhookType.DocumentStatsEvent), key)); + "test1", Sets.newHashSet(WebhookType.DocumentStatsEvent), key)); when(projectIterationDAO.findById(versionId)).thenReturn(version); when(version.getProject()).thenReturn(project); diff --git a/zanata-war/src/test/java/org/zanata/service/impl/TranslationUpdatedManagerTest.java b/zanata-war/src/test/java/org/zanata/service/impl/TranslationUpdatedManagerTest.java index 10f4897498..9c4b7c591f 100644 --- a/zanata-war/src/test/java/org/zanata/service/impl/TranslationUpdatedManagerTest.java +++ b/zanata-war/src/test/java/org/zanata/service/impl/TranslationUpdatedManagerTest.java @@ -106,9 +106,9 @@ public void setup() { webHooks = Lists .newArrayList(new WebHook(project, "http://test.example.com", - Sets.newHashSet(WebhookType.DocumentMilestoneEvent), key), + "test", Sets.newHashSet(WebhookType.DocumentMilestoneEvent), key), new WebHook(project, "http://test.example.com", - Sets.newHashSet(WebhookType.DocumentStatsEvent), key)); + "test", Sets.newHashSet(WebhookType.DocumentStatsEvent), key)); when(person.getAccount()).thenReturn(account); when(account.getUsername()).thenReturn(username); From 9f332b7ccd61e561c4a0bb2121a1ba9dedec4e20 Mon Sep 17 00:00:00 2001 From: Patrick Huang Date: Thu, 6 Oct 2016 14:48:04 +1000 Subject: [PATCH 2/6] ZNTA-1290 - update according to review --- .../org/zanata/action/VersionHomeAction.java | 30 +++++++++++-------- .../service/impl/WebhookServiceImpl.java | 4 +-- .../events/ManuallyTriggeredEvent.java | 20 +++++++++---- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java index 04011410d7..223c2ffc3a 100644 --- a/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java +++ b/zanata-war/src/main/java/org/zanata/action/VersionHomeAction.java @@ -322,6 +322,7 @@ protected boolean include(HLocale elem, String filter) { elem.retrieveDisplayName(), filter); } }; + private List manualWebhooks; public void setVersionSlug(String versionSlug) { this.versionSlug = versionSlug; @@ -339,29 +340,31 @@ public void cancelCopyVersion() { msgs.format("jsf.copyVersion.Cancelled", versionSlug)); } + private List getManualWebhooks() { + if (manualWebhooks == null) { + manualWebhooks = webHookDAO.getWebHooksForType(projectSlug, + WebhookType.ManuallyTriggeredEvent); + } + return manualWebhooks; + } + public boolean canTriggerManualWebhook() { boolean hasLocalePermission = isUserAllowedToTranslateOrReview(selectedLocale); - List webHooks = webHookDAO.getWebHooksForType(projectSlug, - WebhookType.ManuallyTriggeredEvent); - return hasLocalePermission && !webHooks.isEmpty(); + return hasLocalePermission && !getManualWebhooks().isEmpty(); } public void triggerManualWebhookEvent() { - List webHooks = webHookDAO.getWebHooksForType(projectSlug, - WebhookType.ManuallyTriggeredEvent); - - if (selectedLocale != null && !webHooks.isEmpty()) { - webhookService.processTranslationUpdated(projectSlug, versionSlug, - selectedLocale.getLocaleId(), webHooks); + List manualWebhooks = getManualWebhooks(); + if (selectedLocale != null && !manualWebhooks.isEmpty()) { + webhookService.processManualEvent(projectSlug, versionSlug, + selectedLocale.getLocaleId(), manualWebhooks); } } public String getManualWebhookNames() { - List webHooks = webHookDAO.getWebHooksForType(projectSlug, - WebhookType.ManuallyTriggeredEvent); - Set webhookNames = webHooks.stream().map(WebHook::getName).collect( - Collectors.toSet()); + Set webhookNames = getManualWebhooks().stream() + .map(WebHook::getName).collect(Collectors.toSet()); return Joiner.on(", ").skipNulls().join(webhookNames); } @@ -474,6 +477,7 @@ public void resetPageData() { documents = null; version = null; supportedLocale = null; + manualWebhooks = null; loadStatistics(); } diff --git a/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java index 14e4ed71a3..ac83649252 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java @@ -160,9 +160,9 @@ public void processDocumentStats(String username, String projectSlug, } /** - * Process TranslationChangedEvent + * Process ManuallyTriggeredEvent */ - public void processTranslationUpdated(String projectSlug, + public void processManualEvent(String projectSlug, String versionSlug, LocaleId localeId, List webHooks) { ManuallyTriggeredEvent event = new ManuallyTriggeredEvent(authenticatedUser.getUsername(), diff --git a/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java b/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java index d973e147f5..63a19563ab 100644 --- a/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java +++ b/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java @@ -28,7 +28,10 @@ import org.zanata.model.type.WebhookType; /** - * Indicates an event triggered by a user. + * Indicates an webhook event is triggered manually by a user. The fields will + * contain information about the triggering context. e.g. which project, version + * and language the user wants to trigger the event for, as well as the + * triggering user's username. * * @author Patrick Huang pahuang@redhat.com */ @@ -41,15 +44,21 @@ public class ManuallyTriggeredEvent extends WebhookEventType { private String version; private LocaleId locale; - public @Nullable String getProject() { + public + @Nullable + String getProject() { return project; } - public @Nullable String getVersion() { + public + @Nullable + String getVersion() { return version; } - public @Nullable LocaleId getLocale() { + public + @Nullable + LocaleId getLocale() { return locale; } @@ -57,7 +66,8 @@ public String getUsername() { return username; } - public ManuallyTriggeredEvent(String triggeredBy, String project, String version, + public ManuallyTriggeredEvent(String triggeredBy, String project, + String version, LocaleId locale) { this.username = triggeredBy; this.project = project; From 329e9dbfd659730775c60d448ca9cff2d2483ccf Mon Sep 17 00:00:00 2001 From: Patrick Huang Date: Tue, 11 Oct 2016 11:55:16 +1000 Subject: [PATCH 3/6] ZNTA-1290 - update according to review --- .../src/main/java/org/zanata/model/WebHook.java | 4 +++- .../org/zanata/service/impl/WebhookServiceImpl.java | 11 ++++++++--- .../zanata/webhook/events/ManuallyTriggeredEvent.java | 9 ++++++++- .../WEB-INF/layout/project/settings-tab-webhook.xhtml | 2 +- .../main/webapp/resources/zanata/webbook-form.xhtml | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/zanata-model/src/main/java/org/zanata/model/WebHook.java b/zanata-model/src/main/java/org/zanata/model/WebHook.java index 18f4e5cc74..a6b8aa1d50 100644 --- a/zanata-model/src/main/java/org/zanata/model/WebHook.java +++ b/zanata-model/src/main/java/org/zanata/model/WebHook.java @@ -48,6 +48,7 @@ import lombok.Setter; import org.zanata.model.type.WebhookType; +import org.zanata.model.validator.Slug; import org.zanata.model.validator.Url; /** @@ -77,7 +78,8 @@ public class WebHook implements Serializable { @Column(nullable = true) private String secret; - @Size(max = 255) + @Size(max = 20) + @Slug private String name; diff --git a/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java b/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java index ac83649252..344c307919 100644 --- a/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java +++ b/zanata-war/src/main/java/org/zanata/service/impl/WebhookServiceImpl.java @@ -27,6 +27,7 @@ import org.zanata.model.WebHook; import org.zanata.model.type.WebhookType; import org.zanata.security.annotations.Authenticated; +import org.zanata.servlet.annotations.ServerPath; import org.zanata.util.UrlUtil; import org.zanata.webhook.events.DocumentMilestoneEvent; import org.zanata.webhook.events.DocumentStatsEvent; @@ -61,6 +62,10 @@ public class WebhookServiceImpl implements Serializable { private static final int URL_MAX_LENGTH = 255; + @Inject + @ServerPath + private String serverUrl; + /** * Need @Async annotation for TransactionPhase.AFTER_SUCCESS event */ @@ -165,7 +170,7 @@ public void processDocumentStats(String username, String projectSlug, public void processManualEvent(String projectSlug, String versionSlug, LocaleId localeId, List webHooks) { ManuallyTriggeredEvent event = - new ManuallyTriggeredEvent(authenticatedUser.getUsername(), + new ManuallyTriggeredEvent(serverUrl, authenticatedUser.getUsername(), projectSlug, versionSlug, localeId); publishWebhooks(webHooks, event); } @@ -192,12 +197,12 @@ public List getAvailableWebhookTypes() { new WebhookTypeItem(WebhookType.SourceDocumentChangedEvent, msgs.get("jsf.webhookType.SourceDocumentChangedEvent.desc")); - WebhookTypeItem transUpdate = + WebhookTypeItem manualEvent = new WebhookTypeItem(WebhookType.ManuallyTriggeredEvent, msgs.get( "jsf.webhookType.ManuallyTriggeredEvent.desc")); return Lists - .newArrayList(docMilestone, stats, version, maintainer, srcDoc, transUpdate); + .newArrayList(docMilestone, stats, version, maintainer, srcDoc, manualEvent); } public List getDisplayNames(Set types) { diff --git a/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java b/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java index 63a19563ab..777dd7c1d2 100644 --- a/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java +++ b/zanata-war/src/main/java/org/zanata/webhook/events/ManuallyTriggeredEvent.java @@ -39,6 +39,7 @@ public class ManuallyTriggeredEvent extends WebhookEventType { private static final String EVENT_TYPE = WebhookType.ManuallyTriggeredEvent.name(); + private String zanataServer; private String username; private String project; private String version; @@ -66,9 +67,15 @@ public String getUsername() { return username; } - public ManuallyTriggeredEvent(String triggeredBy, String project, + public String getZanataServer() { + return zanataServer; + } + + public ManuallyTriggeredEvent(String zanataServer, String triggeredBy, + String project, String version, LocaleId locale) { + this.zanataServer = zanataServer; this.username = triggeredBy; this.project = project; this.version = version; diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml index 0b2f1d10bc..c64d06b6be 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml @@ -94,7 +94,7 @@
    • - #{webhook.url} + #{not empty webhook.name ? webhook.name + ' - ': ''}#{webhook.url} #{webhookServiceImpl.getDisplayNames(webhook.types)}
    From 47a2d517a61e8e4a969284ca8707bdd9e7fc3b23 Mon Sep 17 00:00:00 2001 From: Patrick Huang Date: Thu, 13 Oct 2016 14:08:33 +1000 Subject: [PATCH 4/6] ZNTA-1290 - update from review --- zanata-model/src/main/java/org/zanata/model/WebHook.java | 8 ++------ .../src/main/java/org/zanata/action/ProjectHome.java | 4 ++-- zanata-war/src/main/resources/messages.properties | 2 +- .../WEB-INF/layout/project/settings-tab-webhook.xhtml | 2 +- .../webapp/WEB-INF/layout/version/languages-tab.xhtml | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/zanata-model/src/main/java/org/zanata/model/WebHook.java b/zanata-model/src/main/java/org/zanata/model/WebHook.java index a6b8aa1d50..bb5a916af6 100644 --- a/zanata-model/src/main/java/org/zanata/model/WebHook.java +++ b/zanata-model/src/main/java/org/zanata/model/WebHook.java @@ -23,7 +23,6 @@ import java.io.Serializable; import java.util.Set; - import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.ElementCollection; @@ -41,16 +40,14 @@ import javax.persistence.UniqueConstraint; import javax.validation.constraints.Size; +import org.zanata.model.type.WebhookType; +import org.zanata.model.validator.Url; import com.google.common.collect.Sets; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.zanata.model.type.WebhookType; -import org.zanata.model.validator.Slug; -import org.zanata.model.validator.Url; - /** * @author Alex Eng aeng@redhat.com */ @@ -79,7 +76,6 @@ public class WebHook implements Serializable { private String secret; @Size(max = 20) - @Slug private String name; diff --git a/zanata-war/src/main/java/org/zanata/action/ProjectHome.java b/zanata-war/src/main/java/org/zanata/action/ProjectHome.java index 223a66863c..8ad2c34528 100644 --- a/zanata-war/src/main/java/org/zanata/action/ProjectHome.java +++ b/zanata-war/src/main/java/org/zanata/action/ProjectHome.java @@ -29,7 +29,6 @@ import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; @@ -760,7 +759,8 @@ public boolean validateSlug(String slug, String componentId) { String validationMessages = ResourceBundle.getBundle("ValidationMessages").getString( "javax.validation.constraints.Slug.message"); - facesMessages.addToControl(componentId, validationMessages); + facesMessages.addToControl(componentId, + validationMessages); return false; } return true; diff --git a/zanata-war/src/main/resources/messages.properties b/zanata-war/src/main/resources/messages.properties index 0d3c5d32e2..c9c9adc46c 100644 --- a/zanata-war/src/main/resources/messages.properties +++ b/zanata-war/src/main/resources/messages.properties @@ -651,7 +651,7 @@ jsf.iteration.ExportTMXIter=Export Version to TMX jsf.iteration.ExportTMX.Language=Export {0} Documents to TMX jsf.iteration.files.NoFiles=No Files Available -jsf.iteration.triggerWebHook=Trigger {0} webhook for {1} +jsf.iteration.triggerWebHook=Trigger manual webhook for {0} ! local directory path where a document is located within the project jsf.iteration.files.Path=Path diff --git a/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml b/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml index c64d06b6be..8e0bf5050b 100644 --- a/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml +++ b/zanata-war/src/main/webapp/WEB-INF/layout/project/settings-tab-webhook.xhtml @@ -94,7 +94,7 @@
    • - #{not empty webhook.name ? webhook.name + ' - ': ''}#{webhook.url} + #{webhook.name}#{webhook.url} #{webhookServiceImpl.getDisplayNames(webhook.types)}