Skip to content

Commit

Permalink
Webhook notification support
Browse files Browse the repository at this point in the history
  • Loading branch information
jmendeza committed Dec 20, 2024
1 parent 9c0fca7 commit 4bfb49a
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.craftercms.deployer.impl.processors.notification;

import org.apache.commons.configuration2.Configuration;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.craftercms.commons.config.ConfigurationException;
import org.craftercms.deployer.api.Deployment;
import org.craftercms.deployer.api.exceptions.DeployerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

public class WebhookNotificationProcessor extends NotificationProcessor<WebhookNotificationProcessor.WebhookMessage> {
private static final Logger logger = LoggerFactory.getLogger(WebhookNotificationProcessor.class);

private static final String URL_CONFIG_KEY = "url";
private static final String METHOD_CONFIG_KEY = "method";
private static final String CONTENT_TYPE_CONFIG_KEY = "contentType";

private String defaultMethod;
private String defaultContentType;

private String method;
private String url;
private String contentType;
private CloseableHttpClient httpClient;


@Override
public void doInit(Configuration config) throws ConfigurationException, DeployerException {
super.doInit(config);
method = config.getString(METHOD_CONFIG_KEY, defaultMethod);
contentType = config.getString(CONTENT_TYPE_CONFIG_KEY, defaultContentType);
url = config.getString(URL_CONFIG_KEY);

httpClient = HttpClients.createDefault();
}

@Override
protected WebhookMessage doCreateMessage(Deployment deployment) {
return new WebhookMessage();
}

@Override
protected void doNotify(WebhookMessage message) throws DeployerException {
logger.info("Sending webhook notification to {} with method {}", url, method);
try {
HttpUriRequest request = createRequest(message);
try (CloseableHttpResponse response = httpClient.execute(request)) {
logger.info("Webhook notification sent with status {}", response.getStatusLine());
}
} catch (IOException e) {
logger.error("Error sending webhook notification", e);
throw new DeployerException(e);
}
}

private HttpUriRequest createRequest(NotificationMessage message) throws DeployerException, UnsupportedEncodingException {
if (method.equalsIgnoreCase("get")) {
return new HttpGet(url);
}
if (method.equalsIgnoreCase("post")) {
HttpPost request = new HttpPost(url);
request.setEntity(new StringEntity(message.getBody(), ContentType.getByMimeType(contentType)));
return request;
}
throw new DeployerException("HTTP method '" + method + " not supported");
}

public void setDefaultMethod(String defaultMethod) {
this.defaultMethod = defaultMethod;
}

public void setDefaultContentType(String defaultContentType) {
this.defaultContentType = defaultContentType;
}

public class WebhookMessage extends NotificationMessage {
}
}
31 changes: 24 additions & 7 deletions src/main/resources/base-target-context.xml
Original file line number Diff line number Diff line change
Expand Up @@ -174,26 +174,37 @@

<bean id="baseNotificationProcessor" abstract="true" parent="deploymentProcessor"
class="org.craftercms.deployer.impl.processors.notification.NotificationProcessor">
<property name="defaultTemplateName" value="${target.notifications.mail.templates.default}"/>
<property name="defaultStatusCondition" value="${target.notifications.mail.status}"/>
<property name="defaultStatusCondition" value="${target.notifications.status}"/>
<property name="defaultDateTimePattern" value="${target.defaultDateTimePattern}"/>
<property name="lastNotificationDateDir" value="${target.notifications.mail.lastDateDir}"/>
<property name="lastNotificationDateDir" value="${target.notifications.lastDateDir}"/>
<property name="defaultLastDateFilenameSuffix"
value="${target.notifications.mail.defaultLastDateFilenameSuffix}"/>
<property name="freeMarkerConfig" ref="mailFreemarkerConfig"/>
<property name="templateSuffix" value="${target.notifications.mail.templates.suffix}"/>
<property name="templateEncoding" value="${target.notifications.mail.encoding}"/>
value="${target.notifications.defaultLastDateFilenameSuffix}"/>
</bean>

<bean id="mailNotificationProcessor" class="org.craftercms.deployer.impl.processors.notification.MailNotificationProcessor"
parent="baseNotificationProcessor">
<property name="defaultTemplateName" value="${target.notifications.mail.templates.default}"/>
<property name="freeMarkerConfig" ref="mailFreemarkerConfig"/>
<property name="templateEncoding" value="${target.notifications.mail.encoding}"/>
<property name="templateSuffix" value="${target.notifications.mail.templates.suffix}"/>
<property name="emailFactory" ref="emailFactory"/>
<property name="objectMapper" ref="objectMapper"/>
<property name="defaultFrom" value="${target.notifications.mail.from}"/>
<property name="defaultSubject" value="${target.notifications.mail.subject}"/>
<property name="defaultHtml" value="${target.notifications.mail.html}"/>
</bean>

<bean id="webhookNotificationProcessor"
class="org.craftercms.deployer.impl.processors.notification.WebhookNotificationProcessor"
parent="baseNotificationProcessor">
<property name="defaultTemplateName" value="${target.notifications.webhook.templates.default}"/>
<property name="freeMarkerConfig" ref="webhookFreemarkerConfig"/>
<property name="templateEncoding" value="${target.notifications.webhook.encoding}"/>
<property name="templateSuffix" value="${target.notifications.webhook.templates.suffix}"/>
<property name="defaultMethod" value="${target.notifications.webhook.defaultMethod}"/>
<property name="defaultContentType" value="${target.notifications.webhook.defaultContentType}"/>
</bean>

<!-- Configuration Profiles -->

<bean id="configurationProvider" class="org.craftercms.deployer.utils.config.profiles.ConfigurationProviderImpl">
Expand Down Expand Up @@ -344,6 +355,12 @@
<property name="itemProcessors" ref="itemProcessors"/>
</bean>

<!-- Webhook -->
<bean id="webhookFreemarkerConfig" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
<property name="templateLoaderPaths"
value="${target.notifications.mail.templates.overrideLocation},${target.notifications.webhook.templates.location}"/>
<property name="defaultEncoding" value="${target.notifications.webhook.encoding}"/>
</bean>
<!-- Mail -->

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
Expand Down
26 changes: 21 additions & 5 deletions src/main/resources/base-target.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,26 @@ target:
- //item/url
- //*[@remote="true"]
notifications:
# For which status should the notifications be sent
status: SUCCESS
# The directory to keep the last notification datetime files
lastDateDir: ${deployer.main.targets.config.folderPath}
# The file to keep track of the last notification email datetime
defaultLastDateFilenameSuffix: -lastNotification
webhook:
# The character encoding
encoding: UTF-8
defaultMethod: post
defaultContentType: application/json
templates:
# The location (Spring URL) of the webhook templates
location: classpath:templates/webhook
# The override location (Spring URL) of the webhook templates
overrideLocation: file:${deployer.main.config.folderPath}/templates/webhook
# The name of the default template
default: slack
# The suffix used to resolve the final name of a template
suffix: -template.ftl
mail:
server:
# The hostname of the mail server used to send notifications
Expand All @@ -233,7 +253,7 @@ target:
templates:
# The location (Spring URL) of the mail templates
location: classpath:templates/mail
# The override location (Spring URL) of the mail templates
# The override location (Spring URL) of the mail templates
overrideLocation: file:${deployer.main.config.folderPath}/templates/mail
# The name of the default mail template
default: default
Expand All @@ -247,10 +267,6 @@ target:
html: true
# For which status should the notifications be sent
status: SUCCESS
# The directory to keep the last notification datetime files
lastDateDir: ${deployer.main.targets.config.folderPath}
# The file to keep track of the last notification email datetime
defaultLastDateFilenameSuffix: -lastNotification
config:
profiles:
aws:
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/templates/targets/remote-target-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,18 @@ target:
{{#list notification_addresses}}
- {{this}}
{{/list}}
- processorName: mailNotificationProcessor
status: ON_ANY_FAILURE
mutePeriodMinutes: {{#if mute_period_minutes}}{{mute_period_minutes}}{{else}}60{{/if}}
to:
{{#list notification_addresses}}
- {{this}}
{{/list}}
{{/if}}
{{#if webhook_url}}
- processorName: webhookNotificationProcessor
status: ON_ANY_FAILURE
mutePeriodMinutes: {{#if mute_period_minutes}}{{mute_period_minutes}}{{else}}60{{/if}}
url: {{webhook_url}}
method: POST
{{/if}}
90 changes: 90 additions & 0 deletions src/main/resources/templates/webhook/slack-template.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"blocks": [
{
"type": "rich_text",
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "A deployment in server ${serverName} has just finished:\n"
}
]
},
{
"type": "rich_text_list",
"style": "bullet",
"indent": 0,
"border": 1,
"elements": [
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "Target ID",
"style": {
"bold": true
}
},
{
"type": "text",
"text": ": ${targetId}"
}
]
},
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "Start",
"style": {
"bold": true
}
},
{
"type": "text",
"text": ": ${start}"
}
]
},
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "End",
"style": {
"bold": true
}
},
{
"type": "text",
"text": ": ${end}"
}
]
},
{
"type": "rich_text_section",
"elements": [
{
"type": "text",
"text": "Status",
"style": {
"bold": true
}
},
{
"type": "text",
"text": ": ${status}"
}
]
}
]
}
]
}
]
}

0 comments on commit 4bfb49a

Please sign in to comment.