Skip to content

Commit 6749c09

Browse files
Niels Wittrup Andersenlangecode
authored andcommitted
Change request to allow more characters in select (#2)
* Update README.md To reflect the actual implementation regarding Sort. * Allow more charecters in select values * Added wildcards to select * Added test of wildcards
1 parent 96e320d commit 6749c09

File tree

4 files changed

+74
-44
lines changed

4 files changed

+74
-44
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,19 @@ selects accounts having a balance between 100 and 1000 (both inclusive).
6464

6565
selects the two accounts having account numbers "123456789" and "234567890".
6666

67+
#### Selecting with wildcards
68+
69+
https://banking.services.sample-bank.dk/accounts?select="name::savings*|name::*loan*"
70+
71+
selects accounts having a name starting with "savings" or containing "loan"
72+
6773
### Sorting API Capability
6874

6975
Sorting is done using a `sort` Query Parameter. Sort order can be either ascending (default) or descending.
7076

7177
The syntax is:
7278

73-
sort="<attribute>+/-|<attribute>+/-|..."
74-
75-
and is equivalent to: `sort="<attribute>::+/-|<attribute>::+/-|..."`.
79+
sort="<attribute>::+/-|<attribute>::+/-|..."
7680

7781
#### Simple Example
7882

@@ -82,7 +86,7 @@ sorts accounts by ascending balance.
8286

8387
#### Sorting on two properties
8488

85-
https://banking.services.sample-bank.dk/accounts?select=balance|lastUpdate-
89+
https://banking.services.sample-bank.dk/accounts?select=balance|lastUpdate::-
8690

8791
sorts accounts by ascending balance and descending lastUpdate.
8892

@@ -176,4 +180,4 @@ with links in the `_links` object and the desired projection in the `_embedded`
176180
The service can choose to return just the accounts including links to transactions under the `_links` object as this is allowed by HAL.
177181
The query parameter `embed` can be used for evolving the service to match the desires of consumers - if many consumers have the same wishes for
178182
what to embed, the service provider should consider including more in the responses and endure the added coupling between this service and the
179-
service that delivers the embedded information. This coupling should of course not be synchronous.
183+
service that delivers the embedded information. This coupling should of course not be synchronous.

src/main/java/io/openapitools/api/capabilities/Sanitizer.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.openapitools.api.capabilities;
22

3+
import java.util.regex.Pattern;
4+
35
/**
46
* API input sanitizer in a rudimental version.
57
*/
@@ -10,12 +12,21 @@ private Sanitizer() {
1012
// reduce scope to avoid default construction
1113
}
1214

15+
/**
16+
* Joined and escaped string of suspicious content.
17+
*
18+
* @return String.
19+
*/
20+
static String regexQuotedSuspiciousContent() {
21+
return Pattern.quote(String.join("", SUSPICIOUS_CONTENT));
22+
}
23+
1324
/**
1425
* A simple sanitizer that needs to be extended and elaborated to cope with injections and
1526
* other things that pose as threats to the services and the data they contain and maintain.
1627
*
17-
* @param input an input string received from a non-trustworthy source (in reality every source)
18-
* @param allowSpaces should the string be stripped for spaces or allow these to stay
28+
* @param input an input string received from a non-trustworthy source (in reality every source)
29+
* @param allowSpaces should the string be stripped for spaces or allow these to stay
1930
* @param allowNumbers can the input contain numbers or not
2031
* @return a sanitized string or an empty string if the sanitation failed for some reason.
2132
*/
@@ -40,7 +51,8 @@ public static String sanitize(String input, boolean allowSpaces, boolean allowNu
4051

4152
/**
4253
* A default version of the santizer used from the local capabilities and thus
43-
* @param input an input string received from a non-trustworthy source (in reality every source)
54+
*
55+
* @param input an input string received from a non-trustworthy source (in reality every source)
4456
* @param allowSpaces should the string be stripped for spaces or allow these to stay
4557
* @return a sanitized string or an empty string if the sanitation failed for some reason
4658
*/

src/main/java/io/openapitools/api/capabilities/Select.java

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* <p>
1010
* Indicated by using an API Query Parameter called {@code select}.
1111
* <p>
12-
* The syntax is: {@code select="<attribute>::<value>|<atribute>::<value>|..."}
12+
* The syntax is: {@code select="<attribute>::<value>|<attribute>::<value>|..."}
1313
* <p>
1414
* Example:
1515
* <p>
@@ -21,26 +21,19 @@
2121
* key for an account.
2222
*/
2323
public final class Select {
24-
private static final Pattern REGEX =
25-
Pattern.compile("^(([a-z][a-zA-Z_0-9]*)::([a-zA-Z_0-9]+)(-|\\+)?)?((\\|[a-z][a-zA-Z_0-9]*)?::([a-zA-Z_0-9]+)(-|\\+)?)*");
24+
private static final String PAIR = "([a-z][a-zA-Z_0-9]*)::([^|" + Sanitizer.regexQuotedSuspiciousContent() + "]+)";
25+
private static final Pattern REGEX = Pattern.compile("^" + PAIR + "(\\|" + PAIR + ")*");
26+
2627
private static final CapabilityParser<Select> PARSER = new CapabilityParser<>(REGEX, Select::parseToken);
2728

28-
private String attribute = "";
29-
private String value = "";
29+
private String attribute;
30+
private String value;
3031

3132
private Select(String attribute, String value) {
3233
this.attribute = attribute;
3334
this.value = value;
3435
}
3536

36-
public String getAttribute() {
37-
return attribute;
38-
}
39-
40-
public String getValue() {
41-
return value;
42-
}
43-
4437
/**
4538
* Delivers a set of Select back containing a number of attributes from the part that is within
4639
* the {@code http:// ..../ some-resource?select="value"} that value may contain one or more attributes
@@ -50,8 +43,6 @@ public String getValue() {
5043
* {@code "attribute::value|anotherAttribute::thatValue|yetAnotherAttribute::thisValue"}
5144
* and so on, it may also take the form {@code "attribute::value|attribute::thatValue|attribute::thisValue"}
5245
* <p>
53-
* The regexp is:
54-
* {@code ^(([a-z][a-zA-Z_0-9]*)::([a-zA-Z_0-9]+)(-|\\+)?)?((\\|[a-z][a-zA-Z_0-9]*)?::([a-zA-Z_0-9]+)(-|\\+)?)*}
5546
*
5647
* @param select the select Query Parameter
5748
* @return a set of attribute(s) and value(s) used for selecting candidates for the response
@@ -62,13 +53,20 @@ public static List<Select> getSelections(String select) {
6253

6354
private static Optional<Select> parseToken(String token) {
6455
String attribute = token.substring(0, token.indexOf(':'));
65-
return Optional.of(new Select(attribute, getValuefrom(token)));
56+
return Optional.of(new Select(attribute, getValueFrom(token)));
6657
}
6758

68-
private static String getValuefrom(String selection) {
59+
private static String getValueFrom(String selection) {
6960
int startsAt = selection.indexOf("::") + "::".length();
7061
int endsAt = !selection.contains("|") ? selection.length() : selection.indexOf('|');
7162
return selection.substring(startsAt, endsAt);
7263
}
7364

65+
public String getAttribute() {
66+
return attribute;
67+
}
68+
69+
public String getValue() {
70+
return value;
71+
}
7472
}

src/test/java/io/openapitools/api/capabilities/SelectTest.java

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,37 @@
11
package io.openapitools.api.capabilities;
22

3+
import org.junit.Test;
4+
35
import java.util.List;
46

57
import static org.junit.Assert.assertEquals;
6-
7-
import org.junit.Test;
8+
import static org.junit.Assert.assertTrue;
89

910
public class SelectTest {
1011

1112

1213
@Test
13-
public void testNoSelection(){
14+
public void testNoSelection() {
1415
List<Select> selections = Select.getSelections(null);
1516
assertEquals(0, selections.size());
1617
}
1718

1819
@Test
19-
public void testEmptySelection(){
20+
public void testEmptySelection() {
2021
List<Select> selections = Select.getSelections("");
2122
assertEquals(0, selections.size());
2223
}
2324

2425
@Test
25-
public void testWrongSelection(){
26+
public void testWrongSelection() {
2627
List<Select> selections = Select.getSelections("::");
2728
assertEquals(0, selections.size());
2829
selections = Select.getSelections("|");
2930
assertEquals(0, selections.size());
3031
}
3132

3233
@Test
33-
public void testIllegalSelection(){
34+
public void testIllegalSelection() {
3435
List<Select> selections = Select.getSelections("?::4");
3536
assertEquals(0, selections.size());
3637
selections = Select.getSelections("3::4");
@@ -44,33 +45,40 @@ public void testIllegalSelection(){
4445
}
4546

4647
@Test
47-
public void testSimpleSelection(){
48+
public void testSimpleSelection() {
4849
List<Select> selections = Select.getSelections("u::4");
4950
assertEquals(1, selections.size());
5051
selections = Select.getSelections("attribute::value");
5152
assertEquals(1, selections.size());
5253
selections = Select.getSelections("a::value");
5354
assertEquals(1, selections.size());
55+
selections = Select.getSelections("attribute:: value");
56+
assertEquals(1, selections.size());
57+
selections = Select.getSelections("attribute:: value ");
58+
assertEquals(1, selections.size());
59+
selections = Select.getSelections("attribute::value ");
60+
assertEquals(1, selections.size());
61+
}
62+
63+
@Test
64+
public void testAdditionalSpecific() {
65+
assertEquals(" S p a c e s ", Select.getSelections("attribute:: S p a c e s ").get(0).getValue());
66+
assertEquals("(parenthesis)", Select.getSelections("attribute::(parenthesis)").get(0).getValue());
67+
assertEquals("forward/slash", Select.getSelections("attribute::forward/slash").get(0).getValue());
5468
}
5569

5670
@Test
57-
public void testSelectionWithWhiteSpaceAttribute(){
71+
public void testSelectionWithWhiteSpaceAttribute() {
5872
List<Select> selections = Select.getSelections("attribute ::value");
5973
assertEquals(0, selections.size());
6074
selections = Select.getSelections("attribute ::value");
6175
assertEquals(0, selections.size());
6276
selections = Select.getSelections(" attribute::value");
6377
assertEquals(0, selections.size());
64-
selections = Select.getSelections("attribute:: value");
65-
assertEquals(0, selections.size());
66-
selections = Select.getSelections("attribute:: value ");
67-
assertEquals(0, selections.size());
68-
selections = Select.getSelections("attribute::value ");
69-
assertEquals(0, selections.size());
7078
}
7179

7280
@Test
73-
public void testSelectionWithSuspiciousCharacters(){
81+
public void testSelectionWithSuspiciousCharacters() {
7482
List<Select> selections = Select.getSelections("attribute or '1' = '1::value");
7583
assertEquals(0, selections.size());
7684
selections = Select.getSelections("attribute or '1' = '1::value");
@@ -90,7 +98,7 @@ public void testSelectionWithSuspiciousCharacters(){
9098
}
9199

92100
@Test
93-
public void testSelectionCriteria(){
101+
public void testSelectionCriteria() {
94102
List<Select> selections = Select.getSelections("attribute::value|otherAttribute::otherValue");
95103
assertEquals(2, selections.size());
96104
boolean attributeValueObserved = false;
@@ -112,7 +120,7 @@ public void testSelectionCriteria(){
112120
}
113121

114122
@Test
115-
public void testSelectionHavingMultipleCriteria(){
123+
public void testSelectionHavingMultipleCriteria() {
116124
List<Select> selections = Select.getSelections("attribute::value|otherAttribute::otherValue");
117125
assertEquals(2, selections.size());
118126
boolean attributeValueObserved = false;
@@ -157,7 +165,7 @@ public void testSelectionHavingMultipleCriteria(){
157165
}
158166

159167
@Test
160-
public void testIllegalSelections(){
168+
public void testIllegalSelections() {
161169
List<Select> selections = Select.getSelections("?::4|a::b");
162170
assertEquals(0, selections.size());
163171
selections = Select.getSelections("?::4|a::b");
@@ -167,5 +175,13 @@ public void testIllegalSelections(){
167175
selections = Select.getSelections("v::g|f::j|u::iio|3::4");
168176
assertEquals(0, selections.size());
169177
}
170-
178+
179+
@Test
180+
public void testWildcards() {
181+
List<Select> selections = Select.getSelections("name::*loan|name::savings*");
182+
assertEquals(2, selections.size());
183+
for (Select sel : selections) {
184+
assertTrue(sel.getValue().equals("*loan") || sel.getValue().equals("savings*"));
185+
}
186+
}
171187
}

0 commit comments

Comments
 (0)