Skip to content

Commit

Permalink
feat: validate INT input with min/max
Browse files Browse the repository at this point in the history
  • Loading branch information
loicmathieu committed Apr 25, 2023
1 parent 3f0e863 commit b276525
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,49 @@
package io.kestra.core.models.flows.input;

import io.kestra.core.models.flows.Input;
import io.kestra.core.models.validations.ManualConstraintViolation;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

import java.util.Set;
import java.util.regex.Pattern;
import javax.validation.ConstraintViolationException;

@SuperBuilder
@Getter
@NoArgsConstructor
public class IntInput extends Input<Integer> {

@Schema(title = "Minimal value.")
Integer min;

@Schema(title = "Maximal value.")
Integer max;

@Override
public void validate(Integer input) throws ConstraintViolationException {
// no validation yet
if (min != null && input.compareTo(min) < 0) {
throw new ConstraintViolationException("Invalid input '" + input + "', it must be more than '" + min + "'",
Set.of(ManualConstraintViolation.of(
"Invalid input",
this,
IntInput.class,
getName(),
input
)));
}

if (max != null && input.compareTo(max) > 0) {
throw new ConstraintViolationException("Invalid input '" + input + "', it must be less than '" + max + "'",
Set.of(ManualConstraintViolation.of(
"Invalid input",
this,
IntInput.class,
getName(),
input
)));
}
}
}
26 changes: 26 additions & 0 deletions core/src/test/java/io/kestra/core/runners/InputsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class InputsTest extends AbstractMemoryRunnerTest {
.put("nested.more.int", "123")
.put("nested.bool", "true")
.put("validatedString", "A123")
.put("validatedInt", "12")
.build();

@Inject
Expand Down Expand Up @@ -204,6 +205,31 @@ void inputValidatedStringBadValue() {
assertThat(e.getMessage(), is("Invalid input 'foo', it must match the pattern 'A\\d+'"));
}

@Test
void inputValidatedInteger() {
Map<String, Object> typeds = typedInputs(inputs);
assertThat(typeds.get("validatedInt"), is(12));
}

@Test
void inputValidatedIntegerBadValue() {
HashMap<String, String> mapMin = new HashMap<>(inputs);
mapMin.put("validatedInt", "9");
ConstraintViolationException e = assertThrows(ConstraintViolationException.class, () -> {
Map<String, Object> typeds = typedInputs(mapMin);
});
assertThat(e.getMessage(), is("Invalid input '9', it must be more than '10'"));

HashMap<String, String> mapMax = new HashMap<>(inputs);
mapMax.put("validatedInt", "21");

e = assertThrows(ConstraintViolationException.class, () -> {
Map<String, Object> typeds = typedInputs(mapMax);
});

assertThat(e.getMessage(), is("Invalid input '21', it must be less than '20'"));
}

@Test
void inputFailed() {
HashMap<String, String> map = new HashMap<>(inputs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,6 @@ void inputsFailed() {
);

assertThat(exception.getConstraintViolations().size(), is(2));
// FIXME the property path changes: io.kestra.core.models.flows.Flow["inputs"]->java.util.ArrayList[0]
// assertThat(exception.getConstraintViolations().stream().filter(r -> r.getPropertyPath().toString().equals("inputs[0].name")).findFirst().orElseThrow().getMessage(), containsString("must match"));
// assertThat(exception.getConstraintViolations().stream().filter(r -> r.getPropertyPath().toString().equals("inputs[0].type")).findFirst().orElseThrow().getMessage(), is("must not be null"));

exception.getConstraintViolations().forEach(
c -> assertThat(c.getMessage(), anyOf(
is("Invalid type: null"),
Expand All @@ -129,9 +125,9 @@ void inputsFailed() {
void inputs() {
Flow flow = this.parse("flows/valids/inputs.yaml");

assertThat(flow.getInputs().size(), is(18));
assertThat(flow.getInputs().size(), is(19));
assertThat(flow.getInputs().stream().filter(Input::getRequired).count(), is(6L));
assertThat(flow.getInputs().stream().filter(r -> !r.getRequired()).count(), is(12L));
assertThat(flow.getInputs().stream().filter(r -> !r.getRequired()).count(), is(13L));
assertThat(flow.getInputs().stream().filter(r -> r.getDefaults() != null).count(), is(1L));
assertThat(flow.getInputs().stream().filter(r -> r instanceof StringInput && ((StringInput)r).getValidator() != null).count(), is(1L));
}
Expand Down
5 changes: 5 additions & 0 deletions core/src/test/resources/flows/valids/inputs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ inputs:
type: STRING
validator: A\d+
required: false
- name: validatedInt
type: INT
min: 10
max: 20
required: false
tasks:
- id: string
type: io.kestra.core.tasks.debugs.Return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.kestra.core.models.flows.Flow;
import io.kestra.core.models.flows.FlowWithSource;
import io.kestra.core.models.flows.Input;
import io.kestra.core.models.flows.input.StringInput;
import io.kestra.core.models.hierarchies.FlowGraph;
import io.kestra.core.models.tasks.Task;
import io.kestra.core.runners.AbstractMemoryRunnerTest;
Expand Down Expand Up @@ -647,7 +648,7 @@ private Flow generateFlow(String friendlyId, String namespace, String inputName)
return Flow.builder()
.id(friendlyId)
.namespace(namespace)
.inputs(ImmutableList.of(Input.builder().type(Input.Type.STRING).name(inputName).build()))
.inputs(ImmutableList.of(StringInput.builder().type(Input.Type.STRING).name(inputName).build()))
.tasks(Collections.singletonList(generateTask("test", "test")))
.build();
}
Expand Down

0 comments on commit b276525

Please sign in to comment.