Skip to content

Commit 74370fd

Browse files
authored
feat: required option for result arguments (#195)
1 parent e912d2d commit 74370fd

15 files changed

Lines changed: 286 additions & 7 deletions

File tree

.github/workflows/maven-checkstyle.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
distribution: 'temurin'
2727
- uses: reviewdog/action-setup@v1
2828
with:
29-
reviewdog_version: v0.17.2
29+
reviewdog_version: v0.21.0
3030
- name: Checkstyle with Maven
3131
run: mvn clean checkstyle:check --fail-never
3232
- name: Run reviewdog

core/src/main/java/tech/illuin/pipeline/annotation/Current.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@
1616
{
1717
String name() default "";
1818

19+
boolean required() default false;
20+
1921
boolean self() default false;
2022
}

core/src/main/java/tech/illuin/pipeline/annotation/Latest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@
1616
{
1717
String name() default "";
1818

19+
boolean required() default false;
20+
1921
boolean self() default false;
2022
}

core/src/main/java/tech/illuin/pipeline/builder/runner_compiler/argument_resolver/mapper_factory/CurrentMapperFactory.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
import tech.illuin.pipeline.annotation.Current;
44
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.method_arguments.MethodArguments;
5+
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.method_arguments.MissingArgument;
56
import tech.illuin.pipeline.commons.Reflection;
67
import tech.illuin.pipeline.step.result.Result;
78
import tech.illuin.pipeline.step.result.Results;
89

910
import java.lang.annotation.Annotation;
1011
import java.lang.reflect.Parameter;
12+
import java.util.NoSuchElementException;
1113
import java.util.Optional;
1214

1315
/**
@@ -32,9 +34,17 @@ public MethodArgumentMapper<T, I> produce(Annotation category, Parameter paramet
3234
{
3335
/* If a name was specified, we rely on it for filtering */
3436
if (filterByName)
35-
return args -> getResults(args, current).current(current.name()).orElseThrow();
37+
{
38+
return args -> getResults(args, current)
39+
.current(current.name())
40+
.or(() -> Optional.of(new MissingArgument("@Current argument with name " + current.name() + " is missing but not marked as required")).filter(ma -> !current.required()))
41+
.orElseThrow(() -> new NoSuchElementException("@Current argument with type " + current.name() + " is missing"));
42+
}
3643
/* Otherwise, we filter by argument type */
37-
return args -> getResults(args, current).current((Class<Result>) parameter.getType()).orElseThrow();
44+
return args -> getResults(args, current)
45+
.current((Class<Result>) parameter.getType())
46+
.or(() -> Optional.of(new MissingArgument("@Current argument with type " + parameter.getType() + " is missing but not marked as required")).filter(ma -> !current.required()))
47+
.orElseThrow(() -> new NoSuchElementException("@Current argument with type " + parameter.getType() + " is missing"));
3848
}
3949

4050
Optional<Class<? extends Result>> optionalArg = Reflection.getOptionalParameter(parameter, Result.class);

core/src/main/java/tech/illuin/pipeline/builder/runner_compiler/argument_resolver/mapper_factory/LatestMapperFactory.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
import tech.illuin.pipeline.annotation.Latest;
44
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.method_arguments.MethodArguments;
5+
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.method_arguments.MissingArgument;
56
import tech.illuin.pipeline.commons.Reflection;
67
import tech.illuin.pipeline.step.result.Result;
78
import tech.illuin.pipeline.step.result.Results;
89

910
import java.lang.annotation.Annotation;
1011
import java.lang.reflect.Parameter;
12+
import java.util.NoSuchElementException;
1113
import java.util.Optional;
1214

1315
/**
@@ -32,9 +34,17 @@ public MethodArgumentMapper<T, I> produce(Annotation category, Parameter paramet
3234
{
3335
/* If a name was specified, we rely on it for filtering */
3436
if (filterByName)
35-
return args -> getResults(args, latest).latest(latest.name()).orElseThrow();
37+
{
38+
return args -> getResults(args, latest)
39+
.latest(latest.name())
40+
.or(() -> Optional.of(new MissingArgument("@Latest argument with name " + latest.name() + " is missing but not marked as required")).filter(ma -> !latest.required()))
41+
.orElseThrow(() -> new NoSuchElementException("@Latest argument with type " + latest.name() + " is missing"));
42+
}
3643
/* Otherwise, we filter by argument type */
37-
return args -> getResults(args, latest).latest((Class<Result>) parameter.getType()).orElseThrow();
44+
return args -> getResults(args, latest)
45+
.latest((Class<Result>) parameter.getType())
46+
.or(() -> Optional.of(new MissingArgument("@Latest argument with type " + parameter.getType() + " is missing but not marked as required")).filter(ma -> !latest.required()))
47+
.orElseThrow(() -> new NoSuchElementException("@Latest argument with type " + parameter.getType() + " is missing"));
3848
}
3949

4050
Optional<Class<? extends Result>> optionalArg = Reflection.getOptionalParameter(parameter, Result.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package tech.illuin.pipeline.builder.runner_compiler.argument_resolver.method_arguments;
2+
3+
import tech.illuin.pipeline.step.result.Result;
4+
5+
public record MissingArgument(String message) implements Result {}

core/src/main/java/tech/illuin/pipeline/sink/runner/SinkRunner.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import org.slf4j.Logger;
44
import org.slf4j.LoggerFactory;
5+
import tech.illuin.pipeline.builder.runner_compiler.CompiledMethod;
56
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.mapper_factory.MethodArgumentMapper;
67
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.method_arguments.MethodArguments;
7-
import tech.illuin.pipeline.builder.runner_compiler.CompiledMethod;
8+
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.method_arguments.MissingArgument;
89
import tech.illuin.pipeline.commons.Reflection;
910
import tech.illuin.pipeline.context.LocalContext;
1011
import tech.illuin.pipeline.observer.descriptor.describable.Describable;
@@ -15,6 +16,7 @@
1516

1617
import java.lang.reflect.InvocationTargetException;
1718
import java.lang.reflect.Method;
19+
import java.util.ArrayList;
1820
import java.util.List;
1921

2022
/**
@@ -55,10 +57,23 @@ public void execute(Output output, LocalContext context) throws Exception
5557
context.observabilityManager(),
5658
context.markerManager()
5759
);
60+
List<MissingArgument> missingArgs = new ArrayList<>();
5861
Object[] arguments = this.argumentMappers.stream()
5962
.map(mapper -> mapper.map(originalArguments))
63+
.peek(arg -> {
64+
if (arg instanceof MissingArgument missing)
65+
missingArgs.add(missing);
66+
})
6067
.toArray()
6168
;
69+
70+
if (!missingArgs.isEmpty())
71+
{
72+
for (MissingArgument missingArg : missingArgs)
73+
logger.trace("Found missing argument for sink {}: {}", this.target.getClass().getName(), missingArg.message());
74+
return;
75+
}
76+
6277
this.method.invoke(this.target, arguments);
6378
}
6479
catch (InvocationTargetException e) {

core/src/main/java/tech/illuin/pipeline/step/result/MultiResult.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
22

33
import java.util.Arrays;
44
import java.util.Collection;
5+
import java.util.Collections;
56

67
public interface MultiResult extends Result
78
{
89
Collection<? extends Result> results();
910

11+
static MultiResult empty()
12+
{
13+
return new EmptyMultiResult();
14+
}
15+
1016
static MultiResult of(Result... results)
1117
{
1218
return of(Arrays.asList(results));
@@ -20,4 +26,12 @@ static MultiResult of(Collection<? extends Result> results)
2026
record AnonymousMultiResult(
2127
Collection<? extends Result> results
2228
) implements MultiResult {}
29+
30+
record EmptyMultiResult() implements MultiResult {
31+
@Override
32+
public Collection<? extends Result> results()
33+
{
34+
return Collections.emptyList();
35+
}
36+
}
2337
}

core/src/main/java/tech/illuin/pipeline/step/runner/StepRunner.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import tech.illuin.pipeline.builder.runner_compiler.CompiledMethod;
66
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.mapper_factory.MethodArgumentMapper;
77
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.method_arguments.MethodArguments;
8+
import tech.illuin.pipeline.builder.runner_compiler.argument_resolver.method_arguments.MissingArgument;
89
import tech.illuin.pipeline.commons.Reflection;
910
import tech.illuin.pipeline.context.LocalContext;
1011
import tech.illuin.pipeline.input.indexer.Indexable;
@@ -17,6 +18,7 @@
1718

1819
import java.lang.reflect.InvocationTargetException;
1920
import java.lang.reflect.Method;
21+
import java.util.ArrayList;
2022
import java.util.Collection;
2123
import java.util.List;
2224
import java.util.Optional;
@@ -59,18 +61,30 @@ public Result execute(T object, I input, Object payload, ResultView results, Loc
5961
context.observabilityManager(),
6062
context.markerManager()
6163
);
64+
List<MissingArgument> missingArgs = new ArrayList<>();
6265
java.lang.Object[] arguments = this.argumentMappers.stream()
6366
.map(mapper -> mapper.map(originalArguments))
67+
.peek(arg -> {
68+
if (arg instanceof MissingArgument missing)
69+
missingArgs.add(missing);
70+
})
6471
.toArray()
6572
;
6673

74+
if (!missingArgs.isEmpty())
75+
{
76+
for (MissingArgument missingArg : missingArgs)
77+
logger.trace("Found missing argument for step {}: {}", this.target.getClass().getName(), missingArg.message());
78+
return MultiResult.empty();
79+
}
80+
6781
Object result = this.method.invoke(this.target, arguments);
6882

6983
/* Valid cases should have corresponding MethodValidators */
7084
if (result instanceof Result)
7185
return (Result) result;
7286
if (result instanceof Optional<?> oResult)
73-
return oResult.map(o -> MultiResult.of((Result) o)).orElseGet(MultiResult::of);
87+
return oResult.map(o -> MultiResult.of((Result) o)).orElseGet(MultiResult::empty);
7488
if (result instanceof Collection<?> cResult)
7589
//noinspection unchecked
7690
return MultiResult.of((Collection<? extends Result>) cResult);

core/src/test/java/tech/illuin/pipeline/sink/annotation/PipelineSinkAnnotationTest.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import java.util.ArrayList;
1717
import java.util.List;
18+
import java.util.NoSuchElementException;
1819

1920
/**
2021
* @author Pierre Lecerf (pierre.lecerf@illuin.tech)
@@ -140,6 +141,28 @@ public void testPipeline__shouldCompile_currentStream()
140141
Assertions.assertEquals("input->stream(input)", collector.get());
141142
}
142143

144+
@Test
145+
public void testPipeline__shouldCompile_currentMissing()
146+
{
147+
StringCollector collector = new StringCollector();
148+
Pipeline<Object> pipeline = Assertions.assertDoesNotThrow(() -> createPipeline_current_missing("test-current-missing", collector));
149+
150+
Assertions.assertDoesNotThrow(() -> pipeline.run("input"));
151+
Assertions.assertDoesNotThrow(pipeline::close);
152+
153+
Assertions.assertNull(collector.get());
154+
}
155+
156+
@Test
157+
public void testPipeline__shouldCompile_currentMissingRequired()
158+
{
159+
StringCollector collector = new StringCollector();
160+
Pipeline<Object> pipeline = Assertions.assertDoesNotThrow(() -> createPipeline_current_missingRequired("test-current-missing-required", collector));
161+
162+
Assertions.assertThrows(NoSuchElementException.class, () -> pipeline.run("input"));
163+
Assertions.assertDoesNotThrow(pipeline::close);
164+
}
165+
143166
@Test
144167
public void testPipeline__shouldCompile_currentNamed()
145168
{
@@ -211,6 +234,28 @@ public void testPipeline__shouldCompile_latestStream()
211234

212235
Assertions.assertEquals("input->stream(input)", collector.get());
213236
}
237+
238+
@Test
239+
public void testPipeline__shouldCompile_latestMissing()
240+
{
241+
StringCollector collector = new StringCollector();
242+
Pipeline<Object> pipeline = Assertions.assertDoesNotThrow(() -> createPipeline_latest_missing("test-latest-missing", collector));
243+
244+
Assertions.assertDoesNotThrow(() -> pipeline.run("input"));
245+
Assertions.assertDoesNotThrow(pipeline::close);
246+
247+
Assertions.assertNull(collector.get());
248+
}
249+
250+
@Test
251+
public void testPipeline__shouldCompile_latestMissingRequired()
252+
{
253+
StringCollector collector = new StringCollector();
254+
Pipeline<Object> pipeline = Assertions.assertDoesNotThrow(() -> createPipeline_latest_missingRequired("test-latest-missing-required", collector));
255+
256+
Assertions.assertThrows(NoSuchElementException.class, () -> pipeline.run("input"));
257+
Assertions.assertDoesNotThrow(pipeline::close);
258+
}
214259

215260
@Test
216261
public void testPipeline__shouldCompile_latestNamed()
@@ -426,6 +471,20 @@ public static Pipeline<Object> createPipeline_currentNamed(String name, StringCo
426471
.build();
427472
}
428473

474+
public static Pipeline<Object> createPipeline_current_missing(String name, StringCollector collector)
475+
{
476+
return Pipeline.of(name)
477+
.registerSink(new SinkWithInputAndCurrent<>(collector))
478+
.build();
479+
}
480+
481+
public static Pipeline<Object> createPipeline_current_missingRequired(String name, StringCollector collector)
482+
{
483+
return Pipeline.of(name)
484+
.registerSink(new SinkWithInputAndCurrent.Required<>(collector))
485+
.build();
486+
}
487+
429488
public static Pipeline<Object> createPipeline_currentOptionalNamed(String name, StringCollector collector)
430489
{
431490
return Pipeline.of(name)
@@ -474,6 +533,20 @@ public static Pipeline<Object> createPipeline_latestNamed(String name, StringCol
474533
.build();
475534
}
476535

536+
public static Pipeline<Object> createPipeline_latest_missing(String name, StringCollector collector)
537+
{
538+
return Pipeline.of(name)
539+
.registerSink(new SinkWithInputAndLatest<>(collector))
540+
.build();
541+
}
542+
543+
public static Pipeline<Object> createPipeline_latest_missingRequired(String name, StringCollector collector)
544+
{
545+
return Pipeline.of(name)
546+
.registerSink(new SinkWithInputAndLatest.Required<>(collector))
547+
.build();
548+
}
549+
477550
public static Pipeline<Object> createPipeline_latestOptionalNamed(String name, StringCollector collector)
478551
{
479552
return Pipeline.of(name)

0 commit comments

Comments
 (0)