Skip to content

Commit c7c8550

Browse files
committed
Parameter lookup feature for script and MVC routes.
1 parent accfc1a commit c7c8550

File tree

13 files changed

+752
-15
lines changed

13 files changed

+752
-15
lines changed

jooby/src/main/java/io/jooby/Context.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
package io.jooby;
77

8+
import io.jooby.internal.ParamLookupImpl;
89
import io.jooby.internal.ReadOnlyContext;
910
import io.jooby.internal.WebSocketSender;
1011

@@ -22,6 +23,7 @@
2223
import java.time.Instant;
2324
import java.time.ZoneId;
2425
import java.time.format.DateTimeFormatter;
26+
import java.util.Arrays;
2527
import java.util.Date;
2628
import java.util.List;
2729
import java.util.Locale;
@@ -715,6 +717,55 @@ public interface Context extends Registry {
715717
*/
716718
@Nonnull FileUpload file(@Nonnull String name);
717719

720+
/* **********************************************************************************************
721+
* Parameter Lookup
722+
* **********************************************************************************************
723+
*/
724+
725+
/**
726+
* Searches for a parameter in the specified sources, in the specified
727+
* order, returning the first non-missing {@link Value}, or a 'missing'
728+
* {@link Value} if none found.
729+
* <p>
730+
* At least one {@link ParamSource} must be specified.
731+
*
732+
* @param name The name of the parameter.
733+
* @param sources Sources to search in.
734+
* @return The first non-missing {@link Value} or a {@link Value} representing
735+
* a missing value if none found.
736+
* @throws IllegalArgumentException If no {@link ParamSource}s are specified.
737+
*/
738+
default Value lookup(String name, ParamSource... sources) {
739+
if (sources.length == 0) {
740+
throw new IllegalArgumentException("No parameter sources were specified.");
741+
}
742+
743+
return Arrays.stream(sources)
744+
.map(source -> source.provider.apply(this, name))
745+
.filter(value -> !value.isMissing())
746+
.findFirst()
747+
.orElseGet(() -> Value.missing(name));
748+
}
749+
750+
/**
751+
* Returns a {@link ParamLookup} instance which is a fluent interface covering
752+
* the functionality of the {@link #lookup(String, ParamSource...)} method.
753+
*
754+
* <pre>{@code
755+
* Value foo = ctx.lookup()
756+
* .inQuery()
757+
* .inPath()
758+
* .get("foo");
759+
* }</pre>
760+
*
761+
* @return A {@link ParamLookup} instance.
762+
* @see ParamLookup
763+
* @see #lookup(String, ParamSource...)
764+
*/
765+
default ParamLookup lookup() {
766+
return new ParamLookupImpl(this);
767+
}
768+
718769
/* **********************************************************************************************
719770
* Request Body
720771
* **********************************************************************************************
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby;
7+
8+
import java.util.Optional;
9+
10+
/**
11+
* Fluent interface allowing to conveniently search context parameters
12+
* in multiple sources.
13+
*
14+
* <pre>{@code
15+
* Value foo = ctx.lookup()
16+
* .inQuery()
17+
* .inPath()
18+
* .get("foo");
19+
* }</pre>
20+
*
21+
* @see Context#lookup()
22+
* @see Context#lookup(String, ParamSource...)
23+
*/
24+
public interface ParamLookup {
25+
26+
/**
27+
* Adds the specified source to the search locations.
28+
*
29+
* @param source The source to add.
30+
* @return This instance.
31+
*/
32+
Stage in(ParamSource source);
33+
34+
/**
35+
* Adds the path parameters to the search locations.
36+
*
37+
* @return This instance.
38+
*/
39+
default Stage inPath() {
40+
return in(ParamSource.PATH);
41+
}
42+
43+
/**
44+
* Adds the header parameters to the search locations.
45+
*
46+
* @return This instance.
47+
*/
48+
default Stage inHeader() {
49+
return in(ParamSource.HEADER);
50+
}
51+
52+
/**
53+
* Adds the cookie parameters to the search locations.
54+
*
55+
* @return This instance.
56+
*/
57+
default Stage inCookie() {
58+
return in(ParamSource.COOKIE);
59+
}
60+
61+
/**
62+
* Adds the flash parameters to the search locations.
63+
*
64+
* @return This instance.
65+
*/
66+
default Stage inFlash() {
67+
return in(ParamSource.FLASH);
68+
}
69+
70+
/**
71+
* Adds the session parameters to the search locations.
72+
*
73+
* @return This instance.
74+
*/
75+
default Stage inSession() {
76+
return in(ParamSource.SESSION);
77+
}
78+
79+
/**
80+
* Adds the query parameters to the search locations.
81+
*
82+
* @return This instance.
83+
*/
84+
default Stage inQuery() {
85+
return in(ParamSource.QUERY);
86+
}
87+
88+
/**
89+
* Adds the form parameters to the search locations.
90+
*
91+
* @return This instance.
92+
*/
93+
default Stage inForm() {
94+
return in(ParamSource.FORM);
95+
}
96+
97+
/**
98+
* Adds the multipart parameters to the search locations.
99+
*
100+
* @return This instance.
101+
*/
102+
default Stage inMultipart() {
103+
return in(ParamSource.MULTIPART);
104+
}
105+
106+
/**
107+
* Fluent interface allowing to conveniently search context parameters
108+
* in multiple sources.
109+
*
110+
* <pre>{@code
111+
* Value foo = ctx.lookup()
112+
* .inQuery()
113+
* .inPath()
114+
* .get("foo");
115+
* }</pre>
116+
*
117+
* @see Context#lookup()
118+
* @see Context#lookup(String, ParamSource...)
119+
*/
120+
interface Stage extends ParamLookup {
121+
122+
/**
123+
* Searches for a parameter in the specified sources, in the specified
124+
* order, returning the first non-missing {@link Value}, or a 'missing'
125+
* {@link Value} if none found.
126+
*
127+
* @param name The name of the parameter.
128+
* @return The first non-missing {@link Value} or a {@link Value} representing
129+
* a missing value if none found.
130+
*/
131+
Value get(String name);
132+
133+
/**
134+
* Wraps the result of {@link #get(String)} in an {@link Optional} if the
135+
* value is a {@link ValueNode} or returns an empty {@link Optional}
136+
* otherwise.
137+
*
138+
* @param name The name of the parameter.
139+
* @return An {@link Optional} wrapping the result of {@link #get(String)}
140+
*/
141+
default Optional<ValueNode> getNode(String name) {
142+
return Optional.of(get(name))
143+
.map(v -> v instanceof ValueNode ? (ValueNode) v : null);
144+
}
145+
}
146+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby;
7+
8+
import java.util.function.BiFunction;
9+
10+
/**
11+
* List of possible parameter sources supported by {@link Context#lookup(String, ParamSource...)}.
12+
*
13+
* @see io.jooby.annotations.Param
14+
* @see Context#lookup(String, ParamSource...)
15+
*/
16+
public enum ParamSource {
17+
18+
/**
19+
* Source equivalent to {@link Context#path(String)}.
20+
*/
21+
PATH(Context::path),
22+
23+
/**
24+
* Source equivalent to {@link Context#header(String)}.
25+
*/
26+
HEADER(Context::header),
27+
28+
/**
29+
* Source equivalent to {@link Context#cookie(String)}.
30+
*/
31+
COOKIE(Context::cookie),
32+
33+
/**
34+
* Source equivalent to {@link Context#flash(String)}.
35+
*/
36+
FLASH(Context::flash),
37+
38+
/**
39+
* Source equivalent to {@link Context#session(String)}.
40+
*/
41+
SESSION(Context::session),
42+
43+
/**
44+
* Source equivalent to {@link Context#query(String)}.
45+
*/
46+
QUERY(Context::query),
47+
48+
/**
49+
* Source equivalent to {@link Context#form(String)}.
50+
*/
51+
FORM(Context::form),
52+
53+
/**
54+
* Source equivalent to {@link Context#multipart(String)}.
55+
*/
56+
MULTIPART(Context::multipart);
57+
58+
final BiFunction<Context, String, Value> provider;
59+
60+
ParamSource(BiFunction<Context, String, Value> provider) {
61+
this.provider = provider;
62+
}
63+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.annotations;
7+
8+
import io.jooby.ParamSource;
9+
10+
import java.lang.annotation.ElementType;
11+
import java.lang.annotation.Retention;
12+
import java.lang.annotation.RetentionPolicy;
13+
import java.lang.annotation.Target;
14+
15+
/**
16+
* Allow access to a parameter from MVC route method and from multiple sources.
17+
* <p>
18+
* The order of {@link ParamSource}s defines the search priority.
19+
*
20+
* <pre>{@code
21+
* public String search(&#64;Param(sources = { ParamSource.QUERY, ParamSource.PATH }) String q) {
22+
* ...
23+
* }
24+
* }</pre>
25+
*/
26+
@Retention(RetentionPolicy.RUNTIME)
27+
@Target(ElementType.PARAMETER)
28+
public @interface Param {
29+
30+
/**
31+
* Parameter name.
32+
*
33+
* @return Parameter name.
34+
*/
35+
String value() default "";
36+
37+
/**
38+
* Parameter sources to search in, the order defines the search priority.
39+
*
40+
* @return Parameter sources to search in.
41+
*/
42+
ParamSource[] sources();
43+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal;
7+
8+
import io.jooby.Context;
9+
import io.jooby.ParamLookup;
10+
import io.jooby.ParamSource;
11+
import io.jooby.Value;
12+
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
16+
public class ParamLookupImpl implements ParamLookup.Stage {
17+
18+
private final Context context;
19+
private final List<ParamSource> chain = new ArrayList<>();
20+
21+
public ParamLookupImpl(Context context) {
22+
this.context = context;
23+
}
24+
25+
@Override
26+
public Stage in(ParamSource source) {
27+
chain.add(source);
28+
return this;
29+
}
30+
31+
@Override
32+
public Value get(String name) {
33+
return context.lookup(name, chain.toArray(new ParamSource[0]));
34+
}
35+
}

0 commit comments

Comments
 (0)