Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
feat(webhook): Add/Remove version webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Eng committed Sep 2, 2016
1 parent 98442d2 commit 9640dd4
Show file tree
Hide file tree
Showing 9 changed files with 462 additions and 126 deletions.
16 changes: 14 additions & 2 deletions zanata-model/src/main/java/org/zanata/model/type/WebhookType.java
@@ -1,11 +1,23 @@
package org.zanata.model.type;

import lombok.Getter;

/**
* Type of Webhook event. See {@link org.zanata.model.WebHook} for usage.
*
* @author Alex Eng <a href="mailto:aeng@redhat.com">aeng@redhat.com</a>
*/
public enum WebhookType {
DocumentMilestoneEvent,
DocumentStatsEvent;
DocumentMilestoneEvent("Translation milestone"),
DocumentStatsEvent("Translation update"),
VersionChangedEvent("Project version"),
ProjectMaintainerChangedEvent("Maintainers update"),
SourceDocumentChangedEvent("Source document");

@Getter
private String displayName;

WebhookType(String displayName) {
this.displayName = displayName;
}
}
140 changes: 114 additions & 26 deletions zanata-war/src/main/java/org/zanata/action/ProjectHome.java
Expand Up @@ -31,6 +31,7 @@
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Nullable;
import javax.enterprise.inject.Any;
Expand All @@ -41,6 +42,7 @@
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
Expand Down Expand Up @@ -221,6 +223,8 @@ public Map<LocaleId, Boolean> getSelectedEnabledLocales() {
@Setter
private Boolean selectedCheckbox = Boolean.TRUE;

private Map<String, List<WebhookType>> webhookUrlMapToType = null;

private List<HLocale> disabledLocales;

public ProjectHome() {
Expand Down Expand Up @@ -1070,77 +1074,161 @@ public List<ValidationAction> getValidationList() {

@Getter
public class WebhookTypeItem {
private String value;
private String name;
private String description;

public WebhookTypeItem(WebhookType type, String desc) {
this.name = type.name();
public WebhookTypeItem(WebhookType webhookType, String desc) {
this.value = webhookType.name();
this.name = webhookType.getDisplayName();
this.description = desc;
}
}

public List<WebhookTypeItem> getWebhookTypes() {
List<WebhookTypeItem> results = Lists.newArrayList();
results.add(new WebhookTypeItem(WebhookType.DocumentMilestoneEvent,
msgs.get("jsf.webhookType.DocumentMilestoneEvent.desc")));
results.add(new WebhookTypeItem(WebhookType.DocumentStatsEvent,
msgs.get("jsf.webhookType.DocumentStatsEvent.desc")));
public List<WebhookTypeItem> getAvailableWebhookTypes() {
WebhookTypeItem docMilestone =
new WebhookTypeItem(WebhookType.DocumentMilestoneEvent,
msgs.get(
"jsf.webhookType.DocumentMilestoneEvent.desc"));

WebhookTypeItem stats =
new WebhookTypeItem(WebhookType.DocumentStatsEvent,
msgs.get("jsf.webhookType.DocumentStatsEvent.desc"));

WebhookTypeItem version =
new WebhookTypeItem(WebhookType.VersionChangedEvent,
msgs.get("jsf.webhookType.VersionChangedEvent.desc"));

WebhookTypeItem maintainer =
new WebhookTypeItem(WebhookType.ProjectMaintainerChangedEvent,
msgs.get("jsf.webhookType.ProjectMaintainerChangedEvent.desc"));

WebhookTypeItem srcDoc =
new WebhookTypeItem(WebhookType.SourceDocumentChangedEvent,
msgs.get("jsf.webhookType.SourceDocumentChangedEvent.desc"));

return Lists
.newArrayList(docMilestone, stats, version, maintainer, srcDoc);
}

@Getter
public class WebhookItem {
// removed : and / from url for use in html
private String id;
private String url;
private List<String> types;

public WebhookItem(String url, List<String> types) {
this.id = url.replaceAll(":", "").replaceAll("/", "");
this.url = url;
this.types = types;
}
}

public List<WebhookItem> getWebhooks() {
if (webhookUrlMapToType == null) {
webhookUrlMapToType = Maps.newHashMap();
for (WebHook webHook : getInstance().getWebHooks()) {
List<WebhookType> webHookTypes = webhookUrlMapToType.get(webHook.getUrl());
if (webHookTypes == null) {
webHookTypes = Lists.newArrayList();
}
webHookTypes.add(webHook.getWebhookType());
webhookUrlMapToType.put(webHook.getUrl(), webHookTypes);
}
}
List<WebhookItem> results = Lists.newArrayList();
results.addAll(webhookUrlMapToType
.entrySet().stream()
.map(entry -> new WebhookItem(entry.getKey(), Lists.transform(
entry.getValue(), new Function<WebhookType, String>() {
@Override
public String apply(WebhookType type) {
return type.getDisplayName();
}
}))).collect(Collectors.toList()));
return results;
}

@Transactional
public void addWebHook(String url, String secret, String strType) {
public void addWebHook(String url, String secret, String strTypes) {
identity.checkPermission(getInstance(), "update");

WebhookType type = WebhookType.valueOf(strType);
if (isValidUrl(url, type)) {
List<WebhookType> types = getTypes(strTypes);
if (isValidUrl(url, types)) {
secret = StringUtils.isBlank(secret) ? null : secret;
WebHook webHook =
List<WebhookType> newTypes = Lists.newArrayList();
for(WebhookType type: types) {
WebHook webHook =
new WebHook(this.getInstance(), url, type, secret);
getInstance().getWebHooks().add(webHook);
getInstance().getWebHooks().add(webHook);
newTypes.add(webHook.getWebhookType());
}
update();
if (webhookUrlMapToType.containsKey(url)) {
webhookUrlMapToType.get(url).addAll(newTypes);
} else {
webhookUrlMapToType.put(url, newTypes);
}
facesMessages.addGlobal(
msgs.format("jsf.project.AddNewWebhook", webHook.getUrl()));
msgs.format("jsf.project.AddNewWebhook", url));
}
}

@Transactional
public void removeWebHook(Long webhookId) {
public void removeWebHook(String url) {
identity.checkPermission(getInstance(), "update");
WebHook webHook = webHookDAO.findById(webhookId);
if (webHook != null) {
getInstance().getWebHooks().remove(webHook);
webHookDAO.makeTransient(webHook);
List<WebHook> webHooks = webHookDAO.findByUrl(url);
if (webHooks != null && !webHooks.isEmpty()) {
for(WebHook webHook: webHooks) {
getInstance().getWebHooks().remove(webHook);
webHookDAO.makeTransient(webHook);
}
webhookUrlMapToType.remove(url);
facesMessages.addGlobal(
msgs.format("jsf.project.RemoveWebhook", webHook.getUrl()));
msgs.format("jsf.project.RemoveWebhook", url));
}
}

public void testWebhook(String url, String secret, String strType) {
public void testWebhook(String url, String secret, String strTypes) {
identity.checkPermission(getInstance(), "update");
WebhookType type = WebhookType.valueOf(strType);
if (isValidUrl(url, type)) {
List<WebhookType> types = getTypes(strTypes);
if (isValidUrl(url, types)) {
TestEvent event =
new TestEvent(identity.getAccountUsername(), getSlug());
WebHooksPublisher
.publish(url, event, Optional.fromNullable(secret));
}
}

private final Function convertToWebHookType = new Function<String, WebhookType>() {
@Override
public WebhookType apply(String input) {
return WebhookType.valueOf(input);
}
};

private List<WebhookType> getTypes(String strTypes) {
return Lists.transform(Lists.newArrayList(strTypes.split(",")),
convertToWebHookType);
}

/**
* Check if url is valid and there is no duplication of url+type
*/
private boolean isValidUrl(String url, WebhookType type) {
private boolean isValidUrl(String url, List<WebhookType> types) {
if (!UrlUtil.isValidUrl(url)) {
facesMessages.addGlobal(SEVERITY_ERROR,
msgs.format("jsf.project.InvalidUrl", url));
return false;
}
for(WebHook webHook: getInstance().getWebHooks()) {
if (StringUtils.equalsIgnoreCase(webHook.getUrl(), url)
&& type.equals(webHook.getWebhookType())) {
&& types.contains(webHook.getWebhookType())) {
facesMessages.addGlobal(SEVERITY_ERROR,
msgs.get("jsf.project.DuplicateUrl"));
msgs.format("jsf.project.DuplicateUrl",
webHook.getUrl(),
webHook.getWebhookType().getDisplayName()));
return false;
}
}
Expand Down
42 changes: 39 additions & 3 deletions zanata-war/src/main/java/org/zanata/action/VersionHome.java
Expand Up @@ -28,6 +28,7 @@

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
Expand Down Expand Up @@ -61,16 +62,20 @@
import org.zanata.model.HLocale;
import org.zanata.model.HProject;
import org.zanata.model.HProjectIteration;
import org.zanata.model.WebHook;
import org.zanata.model.type.WebhookType;
import org.zanata.model.validator.SlugValidator;
import org.zanata.seam.scope.ConversationScopeMessages;
import org.zanata.security.ZanataIdentity;
import org.zanata.service.LocaleService;
import org.zanata.service.SlugEntityService;
import org.zanata.service.ValidationService;
import org.zanata.service.impl.LocaleServiceImpl;
import org.zanata.service.impl.WebHooksPublisher;
import org.zanata.ui.faces.FacesMessages;
import org.zanata.util.ComparatorUtil;
import org.zanata.util.UrlUtil;
import org.zanata.webhook.events.VersionChangedEvent;
import org.zanata.webtrans.shared.model.ValidationAction;
import org.zanata.webtrans.shared.model.ValidationId;
import org.zanata.webtrans.shared.validation.ValidationFactory;
Expand All @@ -82,9 +87,11 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.stream.Collectors;

@Named("versionHome")
@ViewScoped
Expand Down Expand Up @@ -429,10 +436,11 @@ public void setSlug(String slug) {
@Override
@Transactional
public String persist() {
if (!validateSlug(getInputSlugValue(), "slug")) {
String slug = getInputSlugValue();
if (!validateSlug(slug, "slug")) {
return null;
}
getInstance().setSlug(getInputSlugValue());
getInstance().setSlug(slug);
updateProjectType();

HProject project = getProject();
Expand All @@ -450,7 +458,32 @@ public String persist() {

getInstance().getCustomizedValidations().putAll(
project.getCustomizedValidations());
return super.persist();
String result = super.persist();
processWebhookNewVersion(slug, VersionChangedEvent.ChangeType.CREATED,
getInstance().getCreationDate());
return result;
}

private void processWebhookNewVersion(String versionSlug,
VersionChangedEvent.ChangeType changeType, Date date) {
List<WebHook> versionWebhooks =
getProject().getWebHooks().stream().filter(
webHook -> webHook.getWebhookType()
.equals(WebhookType.VersionChangedEvent))
.collect(Collectors.toList());

if (versionWebhooks.isEmpty()) {
return;
}

VersionChangedEvent event =
new VersionChangedEvent(getProjectSlug(),
versionSlug, changeType, date);
for (WebHook webhook : versionWebhooks) {
WebHooksPublisher
.publish(webhook.getUrl(), event,
Optional.fromNullable(webhook.getSecret()));
}
}

@Override
Expand Down Expand Up @@ -559,7 +592,10 @@ public void updateStatus(char initial) {

@Transactional
public void deleteSelf() {
String slug = getInstance().getSlug();
updateStatus('O');
processWebhookNewVersion(slug, VersionChangedEvent.ChangeType.DELETED,
getInstance().getLastChanged());
}

@Transactional
Expand Down
11 changes: 11 additions & 0 deletions zanata-war/src/main/java/org/zanata/dao/WebHookDAO.java
@@ -1,11 +1,14 @@
package org.zanata.dao;

import org.hibernate.Query;
import org.hibernate.Session;

import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
import org.zanata.model.WebHook;

import java.util.List;

@Named("webHookDAO")
@RequestScoped
public class WebHookDAO extends AbstractDAOImpl<WebHook, Long> {
Expand All @@ -17,4 +20,12 @@ public WebHookDAO() {
public WebHookDAO(Session session) {
super(WebHook.class, session);
}

public List<WebHook> findByUrl(String url) {
Query q = getSession().createQuery("from WebHook where url =:url")
.setParameter("url", url)
.setCacheable(true)
.setComment("WebHookDAO.findByUrl");
return q.list();
}
}

0 comments on commit 9640dd4

Please sign in to comment.