generated from kestra-io/plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
571 additions
and
0 deletions.
There are no files selected for viewing
73 changes: 73 additions & 0 deletions
73
src/main/java/io/kestra/plugin/notifications/zulip/ZulipExecution.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package io.kestra.plugin.notifications.zulip; | ||
|
||
import io.kestra.core.models.annotations.Example; | ||
import io.kestra.core.models.annotations.Plugin; | ||
import io.kestra.core.models.tasks.VoidOutput; | ||
import io.kestra.core.runners.RunContext; | ||
import io.kestra.plugin.notifications.ExecutionInterface; | ||
import io.kestra.plugin.notifications.services.ExecutionService; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.Builder; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
import lombok.experimental.SuperBuilder; | ||
|
||
import java.util.Map; | ||
|
||
@SuperBuilder | ||
@ToString | ||
@EqualsAndHashCode | ||
@Getter | ||
@NoArgsConstructor | ||
@Schema( | ||
title = "Send a Zulip message with the execution information", | ||
description = "The message will include a link to the execution page in the UI along with the execution ID, namespace, flow name, the start date, duration and the final status of the execution, and (if failed) the task that led to a failure.\n\n" + | ||
"Use this notification task only in a flow that has a [Flow trigger](https://kestra.io/docs/administrator-guide/monitoring#alerting). Don't use this notification task in `errors` tasks. Instead, for `errors` tasks, use the [ZulipIncomingWebhook](https://kestra.io/plugins/plugin-notifications/tasks/slack/io.kestra.plugin.notifications.slack.slackincomingwebhook) task." | ||
) | ||
@Plugin( | ||
examples = { | ||
@Example( | ||
title = "Send a Zulip notification on a failed flow execution", | ||
full = true, | ||
code = """ | ||
id: failure_alert | ||
namespace: company.team | ||
tasks: | ||
- id: send_alert | ||
type: io.kestra.plugin.notifications.zulip.ZulipExecution | ||
url: "{{ secret('ZULIP_WEBHOOK') }}" # format: https://yourZulipDomain.zulipchat.com/api/v1/external/INTEGRATION_NAME?api_key=API_KEY | ||
channel: "#general" | ||
executionId: "{{trigger.executionId}}" | ||
triggers: | ||
- id: failed_prod_workflows | ||
type: io.kestra.plugin.core.trigger.Flow | ||
conditions: | ||
- type: io.kestra.plugin.core.condition.ExecutionStatusCondition | ||
in: | ||
- FAILED | ||
- WARNING | ||
- type: io.kestra.plugin.core.condition.ExecutionNamespaceCondition | ||
namespace: prod | ||
prefix: true | ||
""" | ||
) | ||
} | ||
) | ||
public class ZulipExecution extends ZulipTemplate implements ExecutionInterface { | ||
@Builder.Default | ||
private final String executionId = "{{ execution.id }}"; | ||
private Map<String, Object> customFields; | ||
private String customMessage; | ||
|
||
@Override | ||
public VoidOutput run(RunContext runContext) throws Exception { | ||
this.templateUri = "zulip-template.peb"; | ||
this.templateRenderMap = ExecutionService.executionMap(runContext, this); | ||
|
||
return super.run(runContext); | ||
} | ||
} |
130 changes: 130 additions & 0 deletions
130
src/main/java/io/kestra/plugin/notifications/zulip/ZulipIncomingWebhook.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
package io.kestra.plugin.notifications.zulip; | ||
|
||
import io.kestra.core.models.annotations.Example; | ||
import io.kestra.core.models.annotations.Plugin; | ||
import io.kestra.core.models.annotations.PluginProperty; | ||
import io.kestra.core.models.tasks.RunnableTask; | ||
import io.kestra.core.models.tasks.Task; | ||
import io.kestra.core.models.tasks.VoidOutput; | ||
import io.kestra.core.runners.RunContext; | ||
import io.micronaut.http.HttpRequest; | ||
import io.micronaut.http.client.netty.DefaultHttpClient; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import jakarta.validation.constraints.NotEmpty; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
import lombok.experimental.SuperBuilder; | ||
|
||
import java.net.URI; | ||
|
||
@SuperBuilder | ||
@ToString | ||
@EqualsAndHashCode | ||
@Getter | ||
@NoArgsConstructor | ||
@Schema( | ||
title = "Send a Zulip message using an Incoming Webhook", | ||
description = "Add this task to send direct Zulip notifications. Check the <a href=\"https://api.zulip.com/messaging/webhooks\">Zulip documentation</a> for more details.." | ||
) | ||
@Plugin( | ||
examples = { | ||
@Example( | ||
title = "Send a Zulip notification on a failed flow execution", | ||
full = true, | ||
code = """ | ||
id: unreliable_flow | ||
namespace: company.team | ||
tasks: | ||
- id: fail | ||
type: io.kestra.plugin.scripts.shell.Commands | ||
runner: PROCESS | ||
commands: | ||
- exit 1 | ||
errors: | ||
- id: alert_on_failure | ||
type: io.kestra.plugin.notifications.zulip.ZulipIncomingWebhook | ||
url: "{{ secret('ZULIP_WEBHOOK') }}" # https://yourZulipDomain.zulipchat.com/api/v1/external/INTEGRATION_NAME?api_key=API_KEY | ||
payload: | | ||
{ | ||
"text": "Failure alert for flow {{ flow.namespace }}.{{ flow.id }} with ID {{ execution.id }}" | ||
} | ||
""" | ||
), | ||
@Example( | ||
title = "Send a Zulip message via incoming webhook with a text argument", | ||
full = true, | ||
code = """ | ||
id: zulip_incoming_webhook | ||
namespace: company.team | ||
tasks: | ||
- id: send_zulip_message | ||
type: io.kestra.plugin.notifications.zulip.ZulipIncomingWebhook | ||
url: "{{ secret('ZULIP_WEBHOOK') }}" # https://yourZulipDomain.zulipchat.com/api/v1/external/INTEGRATION_NAME?api_key=API_KEY | ||
payload: | | ||
{ | ||
"text": "Hello from the workflow {{ flow.id }}" | ||
} | ||
""" | ||
), | ||
@Example( | ||
title = "Send a Zulip message via incoming webhook with a blocks argument, read more on blocks <a href=\"https://api.zulip.com/reference/block-kit/blocks\">here</a>", | ||
full = true, | ||
code = """ | ||
id: zulip_incoming_webhook | ||
namespace: company.team | ||
tasks: | ||
- id: send_zulip_message | ||
type: io.kestra.plugin.notifications.zulip.ZulipIncomingWebhook | ||
url: "{{ secret('ZULIP_WEBHOOK') }}" # format: https://yourZulipDomain.zulipchat.com/api/v1/external/INTEGRATION_NAME?api_key=API_KEY | ||
payload: | | ||
{ | ||
"blocks": [ | ||
{ | ||
"type": "section", | ||
"text": { | ||
"type": "mrkdwn", | ||
"text": "Hello from the workflow *{{ flow.id }}*" | ||
} | ||
} | ||
] | ||
} | ||
""" | ||
), | ||
} | ||
) | ||
public class ZulipIncomingWebhook extends Task implements RunnableTask<VoidOutput> { | ||
@Schema( | ||
title = "Zulip incoming webhook URL", | ||
description = "Check the <a href=\"https://zulip.com/api/incoming-webhooks-overview\">Incoming Webhook Integrations</a> documentation for more details.." | ||
) | ||
@PluginProperty(dynamic = true) | ||
@NotEmpty | ||
private String url; | ||
|
||
@Schema( | ||
title = "Zulip message payload" | ||
) | ||
@PluginProperty(dynamic = true) | ||
protected String payload; | ||
|
||
@Override | ||
public VoidOutput run(RunContext runContext) throws Exception { | ||
String url = runContext.render(this.url); | ||
|
||
try (DefaultHttpClient client = new DefaultHttpClient(URI.create(url))) { | ||
String payload = runContext.render(this.payload); | ||
|
||
runContext.logger().debug("Send Zulip webhook: {}", payload); | ||
|
||
client.toBlocking().retrieve(HttpRequest.POST(url, payload)); | ||
} | ||
|
||
return null; | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
src/main/java/io/kestra/plugin/notifications/zulip/ZulipTemplate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package io.kestra.plugin.notifications.zulip; | ||
|
||
import com.google.common.base.Charsets; | ||
import io.kestra.core.models.annotations.PluginProperty; | ||
import io.kestra.core.models.tasks.VoidOutput; | ||
import io.kestra.core.runners.RunContext; | ||
import io.kestra.core.serializers.JacksonMapper; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
import lombok.experimental.SuperBuilder; | ||
import org.apache.commons.io.IOUtils; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
@SuperBuilder | ||
@ToString | ||
@EqualsAndHashCode | ||
@Getter | ||
@NoArgsConstructor | ||
public abstract class ZulipTemplate extends ZulipIncomingWebhook { | ||
@Schema( | ||
title = "Zulip channel to send the message to" | ||
) | ||
@PluginProperty(dynamic = true) | ||
protected String channel; | ||
|
||
@Schema( | ||
title = "Author of the zulip message" | ||
) | ||
@PluginProperty(dynamic = true) | ||
protected String username; | ||
|
||
@Schema( | ||
title = "Url of the icon to use" | ||
) | ||
@PluginProperty(dynamic = true) | ||
protected String iconUrl; | ||
|
||
@Schema( | ||
title = "Emoji icon to use" | ||
) | ||
@PluginProperty(dynamic = true) | ||
protected String iconEmoji; | ||
|
||
@Schema( | ||
title = "Template to use", | ||
hidden = true | ||
) | ||
@PluginProperty(dynamic = true) | ||
protected String templateUri; | ||
|
||
@Schema( | ||
title = "Map of variables to use for the message template" | ||
) | ||
@PluginProperty(dynamic = true) | ||
protected Map<String, Object> templateRenderMap; | ||
|
||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public VoidOutput run(RunContext runContext) throws Exception { | ||
Map<String, Object> map = new HashMap<>(); | ||
|
||
if (this.templateUri != null) { | ||
String template = IOUtils.toString( | ||
Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream(this.templateUri)), | ||
Charsets.UTF_8 | ||
); | ||
|
||
String render = runContext.render(template, templateRenderMap != null ? templateRenderMap : Map.of()); | ||
map = (Map<String, Object>) JacksonMapper.ofJson().readValue(render, Object.class); | ||
} | ||
|
||
if (this.channel != null) { | ||
map.put("channel", runContext.render(this.channel)); | ||
} | ||
|
||
if (this.username != null) { | ||
map.put("username", runContext.render(this.username)); | ||
} | ||
|
||
if (this.iconUrl != null) { | ||
map.put("icon_url", runContext.render(this.iconUrl)); | ||
} | ||
|
||
if (this.iconEmoji != null) { | ||
map.put("icon_emoji", runContext.render(this.iconEmoji)); | ||
} | ||
|
||
this.payload = JacksonMapper.ofJson().writeValueAsString(map); | ||
|
||
return super.run(runContext); | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
src/main/java/io/kestra/plugin/notifications/zulip/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
@PluginSubGroup( | ||
description = "This sub-group of plugins contains tasks for Zulip notifications.", | ||
categories = PluginSubGroup.PluginCategory.ALERTING | ||
) | ||
package io.kestra.plugin.notifications.zulip; | ||
|
||
import io.kestra.core.models.annotations.PluginSubGroup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
{%- macro title(link, execution) -%} | ||
* | ||
{%- if link is defined -%} | ||
<{{link}}|[{{execution.namespace}}] {{execution.flowId}} ➛ {{execution.state.current}}> | ||
{%- else -%} | ||
[{{execution.namespace}}] {{execution.flowId}} ➛ {{execution.state.current}} | ||
{%- endif -%} | ||
* | ||
{%- endmacro -%} | ||
{ | ||
"text": "{{ title(link, execution) }}\n> {% if firstFailed == false %}Succeeded{% else %}Failed on task `{{firstFailed.taskId}}`{% endif %} after {{duration}}", | ||
"blocks": [ | ||
{% if customMessage is defined %} | ||
{ | ||
"type": "section", | ||
"text": { | ||
"type": "mrkdwn", | ||
"text": {{ customMessage | json }} | ||
} | ||
}, | ||
{% endif %} | ||
{ | ||
"type": "section", | ||
"text": { | ||
"type": "mrkdwn", | ||
"text": "{{ title(link, execution) }}\n> {% if firstFailed == false %}Succeeded{% else %}Failed on task `{{firstFailed.taskId}}`{% endif %} after {{duration}}" | ||
} | ||
{% if link is defined %}, | ||
"accessory": { | ||
"type": "button", | ||
"text": { | ||
"type": "plain_text", | ||
"text": "Details" | ||
}, | ||
"url": "{{link}}" | ||
} | ||
{% endif %} | ||
} | ||
], | ||
"attachments": [ | ||
{ | ||
"color": "{{ execution.state.current == "SUCCESS" ? 'good' : (execution.state.current == "WARNING" ? 'warning' : 'danger') }}", | ||
"fields": [ | ||
{ | ||
"title": "Namespace", | ||
"value": {{execution.namespace | json }}, | ||
"short": true | ||
}, | ||
{ | ||
"title": "Flow ID", | ||
"value": {{execution.flowId | json}}, | ||
"short": true | ||
}, | ||
{ | ||
"title": "Execution ID", | ||
"value": {{execution.id | json}}, | ||
"short": true | ||
}, | ||
{ | ||
"title": "Execution Status", | ||
"value": {{execution.state.current | json }}, | ||
"short": true | ||
} | ||
{% if customFields is defined %} | ||
{% for entry in customFields %} | ||
,{ | ||
"title": {{entry.key | json}}, | ||
"value": {{entry.value | json }}, | ||
"short": true | ||
} | ||
{% endfor %} | ||
{% endif %} | ||
], | ||
"ts": {{ startDate | timestamp }} | ||
} | ||
] | ||
} |
Oops, something went wrong.