Skip to content

Commit

Permalink
refactor(properties): property v2 (#175)
Browse files Browse the repository at this point in the history
* refactor(properties): property v2
Discord
Google
Slack
Opsgenie
PagerDuty
Sendgrid
Sentry
Teams
Telegram
Whatsapp
Zenduty

* fix: unit tests
  • Loading branch information
mgabelle authored Nov 7, 2024
1 parent 6778704 commit 581959f
Show file tree
Hide file tree
Showing 60 changed files with 679 additions and 684 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.kestra.plugin.notifications;

import io.kestra.core.models.annotations.PluginProperty;
import io.kestra.core.models.property.Property;
import io.swagger.v3.oas.annotations.media.Schema;

import java.util.Map;
Expand All @@ -11,18 +11,15 @@ public interface ExecutionInterface {
description = "Default is the current execution, " +
"change it to {{ trigger.executionId }} if you use this task with a Flow trigger to use the original execution."
)
@PluginProperty(dynamic = true)
String getExecutionId();
Property<String> getExecutionId();

@Schema(
title = "Custom fields to be added on notification"
)
@PluginProperty(dynamic = true)
Map<String, Object> getCustomFields();
Property<Map<String, Object>> getCustomFields();

@Schema(
title = "Custom message to be added on notification"
)
@PluginProperty(dynamic = true)
String getCustomMessage();
Property<String> getCustomMessage();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.kestra.core.models.annotations.Example;
import io.kestra.core.models.annotations.Plugin;
import io.kestra.core.models.property.Property;
import io.kestra.core.models.tasks.VoidOutput;
import io.kestra.core.runners.RunContext;
import io.kestra.plugin.notifications.ExecutionInterface;
Expand Down Expand Up @@ -61,14 +62,14 @@
)
public class DiscordExecution extends DiscordTemplate implements ExecutionInterface {
@Builder.Default
private final String executionId = "{{ execution.id }}";
private Map<String, Object> customFields;
private String customMessage;
private final Property<String> executionId = Property.of("{{ execution.id }}");
private Property<Map<String, Object>> customFields;
private Property<String> customMessage;

@Override
public VoidOutput run(RunContext runContext) throws Exception {
this.templateUri = "discord-template.peb";
this.templateRenderMap = ExecutionService.executionMap(runContext, this);
this.templateUri = Property.of("discord-template.peb");
this.templateRenderMap = Property.of(ExecutionService.executionMap(runContext, this));

return super.run(runContext);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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.property.Property;
import io.kestra.core.models.tasks.RunnableTask;
import io.kestra.core.models.tasks.Task;
import io.kestra.core.models.tasks.VoidOutput;
Expand Down Expand Up @@ -101,15 +102,15 @@ public class DiscordIncomingWebhook extends Task implements RunnableTask<VoidOut
@Schema(
title = "Discord message payload"
)
@PluginProperty(dynamic = true)
protected String payload;
protected Property<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);
//First render to get the template, second render to populate the payload
String payload = runContext.render(runContext.render(this.payload).as(String.class).orElse(null));

runContext.logger().debug("Send Discord webhook: {}", payload);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package io.kestra.plugin.notifications.discord;

import io.kestra.core.models.annotations.PluginProperty;
import io.kestra.core.models.property.Property;
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.*;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;

import java.nio.charset.StandardCharsets;
import java.util.*;

import static io.kestra.core.utils.Rethrow.throwFunction;
Expand All @@ -26,26 +27,22 @@ public abstract class DiscordTemplate extends DiscordIncomingWebhook {
title = "Template to use",
hidden = true
)
@PluginProperty(dynamic = true)
protected String templateUri;
protected Property<String> templateUri;

@Schema(
title = "Map of variables to use for the message template"
)
@PluginProperty(dynamic = true)
protected Map<String, Object> templateRenderMap;
protected Property<Map<String, Object>> templateRenderMap;

@Schema(
title = "Webhook username"
)
@PluginProperty(dynamic = true)
protected String username;
protected Property<String> username;

@Schema(
title = "Webhook avatar URL"
)
@PluginProperty(dynamic = true)
protected String avatarUrl;
protected Property<String> avatarUrl;

@Schema(
title = "Adds an embed to the discord notification body"
Expand All @@ -56,34 +53,42 @@ public abstract class DiscordTemplate extends DiscordIncomingWebhook {
@Schema(
title = "Message content"
)
@PluginProperty(dynamic = true)
protected String content;
protected Property<String> content;

@SuppressWarnings("unchecked")
@Override
public VoidOutput run(RunContext runContext) throws Exception {
if (payload != null && !payload.isBlank()) {
final Optional<String> renderedPayload = runContext.render(this.payload).as(String.class);

if (renderedPayload.isPresent() && !renderedPayload.get().isBlank()) {
return super.run(runContext);
}

Map<String, Object> mainMap = new HashMap<>();

if (this.templateUri != null) {
final Optional<String> renderedUri = runContext.render(this.templateUri).as(String.class);

if (renderedUri.isPresent()) {
String template = IOUtils.toString(
Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream(this.templateUri)),
Charsets.UTF_8
);
Objects.requireNonNull(this.getClass()
.getClassLoader().
getResourceAsStream(renderedUri.get())
),
StandardCharsets.UTF_8
);

String render = runContext.render(template, templateRenderMap != null ? templateRenderMap : Map.of());
String render = runContext.render(template, templateRenderMap != null ?
runContext.render(templateRenderMap).asMap(String.class, Object.class) :
Map.of());
mainMap = (Map<String, Object>) JacksonMapper.ofJson().readValue(render, Object.class);
}

if (this.username != null) {
mainMap.put("username", runContext.render(this.username));
mainMap.put("username", runContext.render(this.username).as(String.class).get());
}

if (this.avatarUrl != null) {
mainMap.put("avatar_url", runContext.render(this.avatarUrl));
mainMap.put("avatar_url", runContext.render(this.avatarUrl).as(String.class).get());
}

mainMap.put("tts", false);
Expand All @@ -99,19 +104,19 @@ public VoidOutput run(RunContext runContext) throws Exception {
List<Object> existingEmbeds = (List<Object>) mainMap.get("embeds");

if (!existingEmbeds.isEmpty()) {
Map<String, Object> map = (Map<String, Object>) existingEmbeds.get(0);
embeds.get(0).putAll(map);
Map<String, Object> map = (Map<String, Object>) existingEmbeds.getFirst();
embeds.getFirst().putAll(map);
}
}

mainMap.put("embeds", embeds);
}

if (this.content != null) {
mainMap.put("content", runContext.render(this.content));
mainMap.put("content", runContext.render(this.content).as(String.class).get());
}

this.payload = JacksonMapper.ofJson().writeValueAsString(mainMap);
this.payload = Property.of(JacksonMapper.ofJson().writeValueAsString(mainMap));

return super.run(runContext);
}
Expand All @@ -124,32 +129,27 @@ public static class Embed {
@Schema(
title = "Title"
)
@PluginProperty(dynamic = true)
protected String title;
protected Property<String> title;

@Schema(
title = "Website URL, link title with given URL"
)
@PluginProperty(dynamic = true)
protected String websiteUrl;
protected Property<String> websiteUrl;

@Schema(
title = "Message description"
)
@PluginProperty(dynamic = true)
protected String description;
protected Property<String> description;

@Schema(
title = "Thumbnail URL"
)
@PluginProperty(dynamic = true)
protected String thumbnail;
protected Property<String> thumbnail;

@Schema(
title = "Message author name"
)
@PluginProperty(dynamic = true)
protected String authorName;
protected Property<String> authorName;

@Schema(
title = "RGB color of text",
Expand All @@ -161,8 +161,7 @@ public static class Embed {
@Schema(
title = "Footer text"
)
@PluginProperty(dynamic = true)
protected String footer;
protected Property<String> footer;

private int getColor() {
if (color.length >= 3) {
Expand All @@ -175,43 +174,30 @@ private int getColor() {
return 0;
}

public Map<String, Object> getEmbedMap(RunContext runContext, String avatarUrl) throws Exception {
public Map<String, Object> getEmbedMap(RunContext runContext, Property<String> avatarUrl) throws Exception {
Map<String, Object> embedMap = new HashMap<>();

if (this.title != null) {
embedMap.put("title", runContext.render(this.title));
}
runContext.render(this.title).as(String.class).ifPresent(t -> embedMap.put("title", t));

if (this.description != null) {
embedMap.put("description", runContext.render(this.description));
}
runContext.render(this.description).as(String.class).ifPresent(d -> embedMap.put("description", d));

if (this.websiteUrl != null) {
embedMap.put("url", runContext.render(this.websiteUrl));
}
runContext.render(this.websiteUrl).as(String.class).ifPresent(url -> embedMap.put("url", url));

if (this.thumbnail != null) {
Map<String, String> thumbnailMap = Map.of("url", runContext.render(this.thumbnail));
embedMap.put("thumbnail", thumbnailMap);
}
runContext.render(this.thumbnail).as(String.class).ifPresent(url -> embedMap.put("thumbnail", Map.of("url", url)));

if (this.authorName != null) {
Map<String, String> authorMap = Map.of(
"name", runContext.render(this.authorName),
"url", runContext.render(this.websiteUrl),
"icon_url", runContext.render(avatarUrl)
);
embedMap.put("author", authorMap);
embedMap.put("author", Map.of(
"name", runContext.render(this.authorName).as(String.class).orElse(null),
"url", runContext.render(this.websiteUrl).as(String.class).orElse(null),
"icon_url", runContext.render(avatarUrl).as(String.class).orElse(null)
));
}

if (this.color != null) {
embedMap.put("color", getColor());
}

if (this.footer != null) {
Map<String, String> footerMap = Map.of("text", runContext.render(this.footer));
embedMap.put("footer", footerMap);
}
runContext.render(this.footer).as(String.class).ifPresent(t -> embedMap.put("footer", Map.of("text", t)));

return embedMap;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.kestra.core.models.annotations.Example;
import io.kestra.core.models.annotations.Plugin;
import io.kestra.core.models.property.Property;
import io.kestra.core.models.tasks.VoidOutput;
import io.kestra.core.runners.RunContext;
import io.kestra.plugin.notifications.ExecutionInterface;
Expand Down Expand Up @@ -55,14 +56,14 @@
)
public class GoogleChatExecution extends GoogleChatTemplate implements ExecutionInterface {
@Builder.Default
private final String executionId = "{{ execution.id }}";
private Map<String, Object> customFields;
private String customMessage;
private final Property<String> executionId = Property.of("{{ execution.id }}");
private Property<Map<String, Object>> customFields;
private Property<String> customMessage;

@Override
public VoidOutput run(RunContext runContext) throws Exception {
this.templateUri = "google-chat-template.peb";
this.templateRenderMap = ExecutionService.executionMap(runContext, this);
this.templateUri = Property.of("google-chat-template.peb");
this.templateRenderMap = Property.of(ExecutionService.executionMap(runContext, this));

return super.run(runContext);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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.property.Property;
import io.kestra.core.models.tasks.RunnableTask;
import io.kestra.core.models.tasks.Task;
import io.kestra.core.models.tasks.VoidOutput;
Expand All @@ -17,7 +18,6 @@
import lombok.experimental.SuperBuilder;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.net.URI;

@SuperBuilder
Expand Down Expand Up @@ -88,15 +88,15 @@ public class GoogleChatIncomingWebhook extends Task implements RunnableTask<Void
@Schema(
title = "Google Chat message payload"
)
@PluginProperty(dynamic = true)
protected String payload;
protected Property<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);
//First render to get the template, second render to populate the payload
String payload = runContext.render(runContext.render(this.payload).as(String.class).orElse(null));

runContext.logger().debug("Send Google Chat webhook: {}", payload);

Expand Down
Loading

0 comments on commit 581959f

Please sign in to comment.