Skip to content

Commit

Permalink
refactor: migrate plugin condition to dynamic properties
Browse files Browse the repository at this point in the history
  • Loading branch information
mgabelle committed Jan 24, 2025
1 parent 6bc66d6 commit 4181afe
Show file tree
Hide file tree
Showing 37 changed files with 235 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
import io.kestra.core.exceptions.InternalException;
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.conditions.Condition;
import io.kestra.core.models.conditions.ConditionContext;
import io.kestra.core.models.conditions.ScheduleCondition;
import io.kestra.core.models.property.Property;
import io.kestra.core.runners.RunContext;
import io.kestra.core.utils.DateUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
Expand Down Expand Up @@ -53,34 +54,35 @@ public class DateTimeBetween extends Condition implements ScheduleCondition {
description = "Can be any variable or any valid ISO 8601 datetime. By default, it will use the trigger date."
)
@Builder.Default
@PluginProperty(dynamic = true)
private final String date = "{{ trigger.date }}";
private final Property<String> date = new Property<>("{{ trigger.date }}");

@Schema(
title = "The date to test must be after this one.",
description = "Must be a valid ISO 8601 datetime with the zone identifier (use 'Z' for the default zone identifier)."
)
@PluginProperty
private ZonedDateTime after;
private Property<ZonedDateTime> after;

@Schema(
title = "The date to test must be before this one.",
description = "Must be a valid ISO 8601 datetime with the zone identifier (use 'Z' for the default zone identifier)."
)
@PluginProperty
private ZonedDateTime before;
private Property<ZonedDateTime> before;

@Override
public boolean test(ConditionContext conditionContext) throws InternalException {
String render = conditionContext.getRunContext().render(date, conditionContext.getVariables());
RunContext runContext = conditionContext.getRunContext();
String render = runContext.render(date).as(String.class, conditionContext.getVariables()).orElseThrow();
ZonedDateTime currentDate = DateUtils.parseZonedDateTime(render);

if (this.before != null && this.after != null) {
return currentDate.isAfter(after) && currentDate.isBefore(before);
} else if (this.before != null) {
return currentDate.isBefore(before);
} else if (this.after != null) {
return currentDate.isAfter(after);
var renderedBefore = runContext.render(this.before).as(ZonedDateTime.class, conditionContext.getVariables());
var renderedAfter = runContext.render(this.after).as(ZonedDateTime.class, conditionContext.getVariables());

if (renderedBefore.isPresent() && renderedAfter.isPresent()) {
return currentDate.isAfter(renderedAfter.get()) && currentDate.isBefore(renderedBefore.get());
} else if (renderedBefore.isPresent()) {
return currentDate.isBefore(renderedBefore.get());
} else if (renderedAfter.isPresent()) {
return currentDate.isAfter(renderedAfter.get());
} else {
throw new IllegalConditionEvaluation("Invalid condition with no before nor after");
}
Expand Down
14 changes: 7 additions & 7 deletions core/src/main/java/io/kestra/plugin/core/condition/DayWeek.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import io.kestra.core.exceptions.InternalException;
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.conditions.Condition;
import io.kestra.core.models.conditions.ConditionContext;
import io.kestra.core.models.conditions.ScheduleCondition;
import io.kestra.core.models.property.Property;
import io.kestra.core.runners.RunContext;
import io.kestra.core.utils.DateUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
Expand Down Expand Up @@ -44,19 +45,18 @@ public class DayWeek extends Condition implements ScheduleCondition {
description = "Can be any variable or any valid ISO 8601 datetime. By default, it will use the trigger date."
)
@Builder.Default
@PluginProperty(dynamic = true)
private final String date = "{{ trigger.date }}";
private final Property<String> date = new Property<>("{{ trigger.date }}");

@NotNull
@Schema(title = "The day of week.")
@PluginProperty
private DayOfWeek dayOfWeek;
private Property<DayOfWeek> dayOfWeek;

@Override
public boolean test(ConditionContext conditionContext) throws InternalException {
String render = conditionContext.getRunContext().render(date, conditionContext.getVariables());
RunContext runContext = conditionContext.getRunContext();
String render = runContext.render(date).as(String.class, conditionContext.getVariables()).orElseThrow();
LocalDate currentDate = DateUtils.parseLocalDate(render);

return currentDate.getDayOfWeek().equals(this.dayOfWeek);
return currentDate.getDayOfWeek().equals(runContext.render(dayOfWeek).as(DayOfWeek.class, conditionContext.getVariables()).orElseThrow());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import io.kestra.core.models.conditions.Condition;
import io.kestra.core.models.conditions.ConditionContext;
import io.kestra.core.models.conditions.ScheduleCondition;
import io.kestra.core.models.property.Property;
import io.kestra.core.runners.RunContext;
import io.kestra.core.utils.DateUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
Expand Down Expand Up @@ -46,38 +48,39 @@ public class DayWeekInMonth extends Condition implements ScheduleCondition {
description = "Can be any variable or any valid ISO 8601 datetime. By default, it will use the trigger date."
)
@Builder.Default
@PluginProperty(dynamic = true)
private final String date = "{{ trigger.date }}";
private final Property<String> date = new Property<>("{{ trigger.date }}");

@NotNull
@Schema(title = "The day of week.")
@PluginProperty
private DayOfWeek dayOfWeek;
private Property<DayOfWeek> dayOfWeek;

@NotNull
@Schema(title = "Are you looking for the first or the last day in the month?")
@PluginProperty
private DayWeekInMonth.DayInMonth dayInMonth;
private Property<DayWeekInMonth.DayInMonth> dayInMonth;

@Override
public boolean test(ConditionContext conditionContext) throws InternalException {
String render = conditionContext.getRunContext().render(date, conditionContext.getVariables());
RunContext runContext = conditionContext.getRunContext();
String render = runContext.render(date).as(String.class, conditionContext.getVariables()).orElseThrow();
LocalDate currentDate = DateUtils.parseLocalDate(render);
LocalDate computed;

if (dayInMonth.equals(DayInMonth.FIRST)) {
computed = currentDate.with(TemporalAdjusters.firstInMonth(dayOfWeek));
} else if (dayInMonth.equals(DayInMonth.LAST)) {
computed = currentDate.with(TemporalAdjusters.lastInMonth(dayOfWeek));
} else if (dayInMonth.equals(DayInMonth.SECOND)) {
computed = currentDate.with(TemporalAdjusters.firstInMonth(dayOfWeek)).with(TemporalAdjusters.next(dayOfWeek));
} else if (dayInMonth.equals(DayInMonth.THIRD)) {
computed = currentDate.with(TemporalAdjusters.firstInMonth(dayOfWeek)).with(TemporalAdjusters.next(dayOfWeek)).with(TemporalAdjusters.next(dayOfWeek));
} else if (dayInMonth.equals(DayInMonth.FOURTH)) {
computed = currentDate.with(TemporalAdjusters.firstInMonth(dayOfWeek)).with(TemporalAdjusters.next(dayOfWeek)).with(TemporalAdjusters.next(dayOfWeek)).with(TemporalAdjusters.next(dayOfWeek));
} else {
throw new IllegalArgumentException("Invalid dayInMonth");
}
var renderedDayOfWeek = runContext.render(this.dayOfWeek).as(DayOfWeek.class).orElseThrow();
LocalDate computed = switch (runContext.render(dayInMonth).as(DayWeekInMonth.DayInMonth.class, conditionContext.getVariables()).orElseThrow()) {
case DayInMonth.FIRST -> currentDate.with(TemporalAdjusters.firstInMonth(renderedDayOfWeek));
case DayInMonth.LAST -> currentDate.with(TemporalAdjusters.lastInMonth(renderedDayOfWeek));
case DayInMonth.SECOND ->
currentDate.with(TemporalAdjusters.firstInMonth(renderedDayOfWeek))
.with(TemporalAdjusters.next(renderedDayOfWeek));
case DayInMonth.THIRD ->
currentDate.with(TemporalAdjusters.firstInMonth(renderedDayOfWeek))
.with(TemporalAdjusters.next(renderedDayOfWeek))
.with(TemporalAdjusters.next(renderedDayOfWeek));
case DayInMonth.FOURTH ->
currentDate.with(TemporalAdjusters.firstInMonth(renderedDayOfWeek))
.with(TemporalAdjusters.next(renderedDayOfWeek))
.with(TemporalAdjusters.next(renderedDayOfWeek))
.with(TemporalAdjusters.next(renderedDayOfWeek));
};

return computed.isEqual(currentDate);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import io.kestra.core.exceptions.IllegalConditionEvaluation;
import io.kestra.core.exceptions.InternalException;
import io.kestra.core.models.annotations.PluginProperty;
import io.kestra.core.models.property.Property;
import io.kestra.core.runners.RunContext;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
Expand Down Expand Up @@ -41,20 +42,20 @@
public class ExecutionFlow extends Condition {
@NotNull
@Schema(title = "The namespace of the flow.")
@PluginProperty
private String namespace;
private Property<String> namespace;

@NotNull
@Schema(title = "The flow id.")
@PluginProperty
private String flowId;
private Property<String> flowId;

@Override
public boolean test(ConditionContext conditionContext) throws InternalException {
if (conditionContext.getExecution() == null) {
throw new IllegalConditionEvaluation("Invalid condition with null execution");
}

return conditionContext.getExecution().getNamespace().equals(this.namespace) && conditionContext.getExecution().getFlowId().equals(this.flowId);
RunContext runContext = conditionContext.getRunContext();
return conditionContext.getExecution().getNamespace().equals(runContext.render(this.namespace).as(String.class, conditionContext.getVariables()).orElseThrow())
&& conditionContext.getExecution().getFlowId().equals(runContext.render(this.flowId).as(String.class, conditionContext.getVariables()).orElseThrow());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.kestra.core.exceptions.IllegalConditionEvaluation;
import io.kestra.core.exceptions.InternalException;
import io.kestra.core.models.annotations.PluginProperty;
import io.kestra.core.models.property.Property;
import io.kestra.core.runners.RunContext;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.experimental.SuperBuilder;
Expand Down Expand Up @@ -42,31 +44,33 @@ public class ExecutionNamespace extends Condition {
@Schema(
title = "String against which to match the execution namespace depending on the provided comparison."
)
@PluginProperty
private String namespace;
private Property<String> namespace;

@Schema(
title = "Comparison to use when checking if namespace matches. If not provided, it will use `EQUALS` by default."
)
@PluginProperty
private Comparison comparison;
private Property<Comparison> comparison;

@Schema(
title = "Whether to look at the flow namespace by prefix. Shortcut for `comparison: PREFIX`.",
description = "Only used when `comparison` is not set"
)
@PluginProperty
@Builder.Default
private boolean prefix = false;
private Property<Boolean> prefix = Property.of(false);

@Override
public boolean test(ConditionContext conditionContext) throws InternalException {
if (conditionContext.getExecution() == null) {
throw new IllegalConditionEvaluation("Invalid condition with null execution");
}

return Optional.ofNullable(this.comparison).orElse(prefix ? Comparison.PREFIX : Comparison.EQUALS)
.test(conditionContext.getExecution().getNamespace(), this.namespace);
RunContext runContext = conditionContext.getRunContext();
var renderedPrefix = runContext.render(this.prefix).as(Boolean.class).orElseThrow();
var renderedNamespace = runContext.render(this.namespace).as(String.class).orElseThrow();

return runContext.render(this.comparison).as(Comparison.class)
.orElse(Boolean.TRUE.equals(renderedPrefix) ? Comparison.PREFIX : Comparison.EQUALS)
.test(conditionContext.getExecution().getNamespace(), renderedNamespace);
}

public enum Comparison {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.kestra.core.models.conditions.ConditionContext;
import io.kestra.core.models.conditions.ScheduleCondition;
import io.kestra.core.models.executions.Execution;
import io.kestra.core.models.property.Property;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
Expand Down Expand Up @@ -50,9 +51,7 @@ public class ExecutionOutputs extends Condition implements ScheduleCondition {
private static final String OUTPUTS_VAR = "outputs";

@NotNull
@NotEmpty
@PluginProperty
private String expression;
private Property<String> expression;

/** {@inheritDoc} **/
@SuppressWarnings("unchecked")
Expand All @@ -68,8 +67,8 @@ public boolean test(ConditionContext conditionContext) throws InternalException
Map.of(TRIGGER_VAR, Map.of(OUTPUTS_VAR, conditionContext.getExecution().getOutputs()))
);

String render = conditionContext.getRunContext().render(expression, variables);
return !(render.isBlank() || render.isEmpty() || render.trim().equals("false"));
String render = conditionContext.getRunContext().render(expression).as(String.class, variables).orElseThrow();
return !(render.isBlank() || render.trim().equals("false"));
}

private boolean hasNoOutputs(final Execution execution) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.kestra.core.exceptions.IllegalConditionEvaluation;
import io.kestra.core.exceptions.InternalException;
import io.kestra.core.models.annotations.PluginProperty;
import io.kestra.core.models.property.Property;
import io.kestra.core.runners.RunContext;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
Expand Down Expand Up @@ -43,13 +45,11 @@
public class ExecutionStatus extends Condition {
@Valid
@Schema(title = "List of states that are authorized.")
@PluginProperty
private List<State.Type> in;
private Property<List<State.Type>> in;

@Valid
@Schema(title = "List of states that aren't authorized.")
@PluginProperty
private List<State.Type> notIn;
private Property<List<State.Type>> notIn;

@Override
public boolean test(ConditionContext conditionContext) throws InternalException {
Expand All @@ -59,11 +59,14 @@ public boolean test(ConditionContext conditionContext) throws InternalException

boolean result = true;

if (this.in != null && !this.in.contains(conditionContext.getExecution().getState().getCurrent())) {
RunContext runContext = conditionContext.getRunContext();
var stateInRendered = runContext.render(this.in).asList(State.Type.class, conditionContext.getVariables());
if (!stateInRendered.isEmpty() && !stateInRendered.contains(conditionContext.getExecution().getState().getCurrent())) {
result = false;
}

if (this.notIn != null && this.notIn.contains(conditionContext.getExecution().getState().getCurrent())) {
var stateNotInRendered = runContext.render(this.notIn).asList(State.Type.class, conditionContext.getVariables());
if (!stateNotInRendered.isEmpty() && stateNotInRendered.contains(conditionContext.getExecution().getState().getCurrent())) {
result = false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.kestra.core.models.conditions.Condition;
import io.kestra.core.models.conditions.ConditionContext;
import io.kestra.core.models.conditions.ScheduleCondition;
import io.kestra.core.models.property.Property;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
Expand Down Expand Up @@ -42,13 +43,11 @@
)
public class Expression extends Condition implements ScheduleCondition {
@NotNull
@NotEmpty
@PluginProperty
private String expression;
private Property<String> expression;

@Override
public boolean test(ConditionContext conditionContext) throws InternalException {
String render = conditionContext.getRunContext().render(expression, conditionContext.getVariables());
return !(render.isBlank() || render.isEmpty() || render.trim().equals("false"));
String render = conditionContext.getRunContext().render(expression).as(String.class, conditionContext.getVariables()).orElseThrow();
return !(render.isBlank() || render.trim().equals("false"));
}
}
Loading

0 comments on commit 4181afe

Please sign in to comment.