Skip to content

Commit c2e1d56

Browse files
authored
Merge branch 'main' into set-permissions
2 parents cbc9fb3 + a44c6b4 commit c2e1d56

4 files changed

Lines changed: 88 additions & 41 deletions

File tree

datamodel/odata-client/src/main/java/com/sap/cloud/sdk/datamodel/odata/client/ODataProtocol.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
import java.time.temporal.ChronoField;
1212
import java.util.AbstractMap;
1313
import java.util.Map;
14+
import java.util.Set;
1415
import java.util.UUID;
1516
import java.util.function.Function;
17+
import java.util.function.Predicate;
1618

1719
import javax.annotation.Nonnull;
1820

@@ -52,6 +54,20 @@ public interface ODataProtocol extends ODataResponseDescriptor, ODataLiteralSeri
5254
@Nonnull
5355
Map.Entry<String, String> getQueryOptionInlineCount( boolean optionEnabled );
5456

57+
/**
58+
* Check whether custom query parameter can be attached to a structured-query.
59+
*
60+
* @param isRoot
61+
* indicates whether the structured-query is the root query or a nested query.
62+
* @param key
63+
* the key of the custom query parameter.
64+
* @return true if the custom query parameter is allowed, false otherwise.
65+
*/
66+
default boolean allowCustomQueryParameter( final boolean isRoot, @Nonnull final String key )
67+
{
68+
return isRoot;
69+
}
70+
5571
/**
5672
* OData protocol v2.
5773
*/
@@ -190,6 +206,14 @@ public String toString()
190206
{
191207
return "OData " + protocolVersion;
192208
}
209+
210+
@Override
211+
public boolean allowCustomQueryParameter( final boolean isRoot, @Nonnull final String key )
212+
{
213+
final Predicate<String> expandOption =
214+
Set.of("$count", "$filter", "$orderby", "$search", "$skip", "$top")::contains;
215+
return isRoot || expandOption.test(key);
216+
}
193217
}
194218

195219
/**

datamodel/odata-client/src/main/java/com/sap/cloud/sdk/datamodel/odata/client/query/QuerySerializer.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ static String serializeAndEncodeQuery( @Nonnull final StructuredQuery query, fin
4949
.map(q -> String.format(parameterString, q))
5050
.forEach(parameters::add));
5151

52-
if( query.isRoot() ) {
53-
query
54-
.getCustomParameters()
55-
.forEach(( key, value ) -> parameters.add(key + "=" + conditionalEncode(value, applyEncoding)));
52+
for( final Map.Entry<String, String> customParam : query.getCustomParameters().entrySet() ) {
53+
final String key = customParam.getKey();
54+
if( query.getProtocol().allowCustomQueryParameter(query.isRoot(), key) ) {
55+
parameters.add(key + "=" + conditionalEncode(customParam.getValue(), applyEncoding));
56+
}
5657
}
5758

5859
final String queryElementSeparator = query.isRoot() ? SEPARATOR_ROOT_QUERY : SEPARATOR_SUB_QUERY;

datamodel/odata-client/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/request/ODataRequestReadTest.java

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
77
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
88
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
9+
import static com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol.V2;
10+
import static com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol.V4;
911
import static org.assertj.core.api.Assertions.assertThat;
1012
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
1113
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -23,7 +25,6 @@
2325
import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination;
2426
import com.sap.cloud.sdk.cloudplatform.connectivity.Destination;
2527
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpClientAccessor;
26-
import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol;
2728
import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference;
2829
import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath;
2930
import com.sap.cloud.sdk.datamodel.odata.client.expression.OrderExpression;
@@ -63,7 +64,7 @@ void testQueryParameters()
6364
final String queryString = "$select=select1&$expand=expand1,expand2($select=nestedSelect;$top=10)&$top=1";
6465

6566
final ODataRequestRead request =
66-
new ODataRequestRead(ODATA_SERVICE_PATH, ODATA_ENTITY_COLLECTION, queryString, ODataProtocol.V4);
67+
new ODataRequestRead(ODATA_SERVICE_PATH, ODATA_ENTITY_COLLECTION, queryString, V4);
6768
request.addQueryParameter("query-param1", "qp1");
6869
request.addQueryParameter("query-param2", "qp2");
6970
request.addHeader("header-key1", "hk1");
@@ -91,22 +92,37 @@ void testQueryParameters()
9192
@Test
9293
void testV4QueryExpand()
9394
{
94-
final StructuredQuery query = StructuredQuery.onEntity("Movies", ODataProtocol.V4);
95-
final StructuredQuery subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", ODataProtocol.V4);
96-
final StructuredQuery subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", ODataProtocol.V4);
97-
final StructuredQuery subQuery3 = StructuredQuery.asNestedQueryOnProperty("relatedBook", ODataProtocol.V4);
95+
final StructuredQuery query = StructuredQuery.onEntity("Movies", V4);
96+
final StructuredQuery subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V4);
97+
final StructuredQuery subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", V4);
98+
final StructuredQuery subQuery3 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V4);
9899
subQuery2.select(subQuery3);
99100
subQuery1.select(subQuery2);
100101
query.select(subQuery1);
101102
assertThat(query.getQueryString()).isEqualTo("$expand=relatedBook($expand=relatedMovies($expand=relatedBook))");
102103
}
103104

105+
@Test
106+
void testV4QueryExpandWithOptions()
107+
{
108+
final StructuredQuery query = StructuredQuery.onEntity("Movies", V4),
109+
subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V4).skip(3).top(10),
110+
subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", V4).orderBy("Duration", Order.DESC),
111+
subQuery3 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V4).withInlineCount();
112+
subQuery2.select(subQuery3);
113+
subQuery1.select(subQuery2);
114+
query.select(subQuery1);
115+
assertThat(query.getQueryString())
116+
.isEqualTo(
117+
"$expand=relatedBook($expand=relatedMovies($expand=relatedBook($count=true);$orderby=Duration desc);$top=10;$skip=3)");
118+
}
119+
104120
@Test
105121
void testV4QuerySelectAndFilter()
106122
{
107-
final StructuredQuery query = StructuredQuery.onEntity("Movies", ODataProtocol.V4);
108-
final StructuredQuery subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", ODataProtocol.V4);
109-
final StructuredQuery subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", ODataProtocol.V4);
123+
final StructuredQuery query = StructuredQuery.onEntity("Movies", V4);
124+
final StructuredQuery subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V4);
125+
final StructuredQuery subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", V4);
110126
subQuery1.select(subQuery2);
111127
query.select(subQuery1);
112128

@@ -128,10 +144,10 @@ void testV4QuerySelectAndFilter()
128144
@Test
129145
void testV2QueryExpand()
130146
{
131-
final StructuredQuery query = StructuredQuery.onEntity("Movies", ODataProtocol.V2);
132-
final StructuredQuery subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", ODataProtocol.V2);
133-
final StructuredQuery subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", ODataProtocol.V2);
134-
final StructuredQuery subQuery3 = StructuredQuery.asNestedQueryOnProperty("relatedBook", ODataProtocol.V2);
147+
final StructuredQuery query = StructuredQuery.onEntity("Movies", V2);
148+
final StructuredQuery subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V2);
149+
final StructuredQuery subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", V2);
150+
final StructuredQuery subQuery3 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V2);
135151
subQuery2.select(subQuery3);
136152
subQuery1.select(subQuery2);
137153
query.select(subQuery1);
@@ -143,12 +159,26 @@ void testV2QueryExpand()
143159
*/
144160
}
145161

162+
@Test
163+
void testV2QueryExpandWithOptionsIgnored()
164+
{
165+
final StructuredQuery query = StructuredQuery.onEntity("Movies", V2),
166+
subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V2).skip(3).top(10),
167+
subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", V2).orderBy("Duration", Order.DESC),
168+
subQuery3 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V2).withInlineCount();
169+
subQuery2.select(subQuery3);
170+
subQuery1.select(subQuery2);
171+
query.select(subQuery1);
172+
assertThat(query.getQueryString())
173+
.isEqualTo("$expand=relatedBook,relatedBook/relatedMovies,relatedBook/relatedMovies/relatedBook");
174+
}
175+
146176
@Test
147177
void testV2QuerySelectAndFilter()
148178
{
149-
final StructuredQuery query = StructuredQuery.onEntity("Movies", ODataProtocol.V2);
150-
final StructuredQuery subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", ODataProtocol.V2);
151-
final StructuredQuery subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", ODataProtocol.V2);
179+
final StructuredQuery query = StructuredQuery.onEntity("Movies", V2);
180+
final StructuredQuery subQuery1 = StructuredQuery.asNestedQueryOnProperty("relatedBook", V2);
181+
final StructuredQuery subQuery2 = StructuredQuery.asNestedQueryOnProperty("relatedMovies", V2);
152182
subQuery1.select(subQuery2);
153183
query.select(subQuery1);
154184

@@ -178,8 +208,7 @@ void testExceptionWhenUnencodedQueryString()
178208
final String unencodedQuery = "$orderby=name asc,ID";
179209

180210
assertThatExceptionOfType(IllegalArgumentException.class)
181-
.isThrownBy(
182-
() -> new ODataRequestRead(servicePath, entityName, unencodedQuery, ODataProtocol.V4).getRelativeUri());
211+
.isThrownBy(() -> new ODataRequestRead(servicePath, entityName, unencodedQuery, V4).getRelativeUri());
183212
}
184213

185214
@Test
@@ -192,8 +221,7 @@ void testGuavaUrlEscaperEscapedQueryString()
192221

193222
final String encodedQuery = UrlEscapers.urlFragmentEscaper().escape(unencodedQuery);
194223

195-
final URI relativeUri =
196-
new ODataRequestRead(servicePath, entityName, encodedQuery, ODataProtocol.V4).getRelativeUri();
224+
final URI relativeUri = new ODataRequestRead(servicePath, entityName, encodedQuery, V4).getRelativeUri();
197225

198226
final String expectedUri = "/odata/v4/Service/Authors?$orderby=name%20asc,ID";
199227

@@ -208,15 +236,14 @@ void testBuildQueryStringWithStructuredQueryV2()
208236

209237
final StructuredQuery structuredQuery =
210238
StructuredQuery
211-
.onEntity(entityName, ODataProtocol.V2)
239+
.onEntity(entityName, V2)
212240
.filter(FieldReference.of("philosphy").equalTo("Yin & Yang"))
213241
.orderBy(OrderExpression.of("name", Order.ASC).and("ID", Order.ASC))
214242
.withInlineCount();
215243

216244
final String encodedQuery = structuredQuery.getEncodedQueryString();
217245

218-
final URI relativeUri =
219-
new ODataRequestRead(servicePath, entityName, encodedQuery, ODataProtocol.V2).getRelativeUri();
246+
final URI relativeUri = new ODataRequestRead(servicePath, entityName, encodedQuery, V2).getRelativeUri();
220247

221248
final String expectedUri =
222249
"/odata/v2/Service/Authors?$filter=(philosphy%20eq%20'Yin%20%26%20Yang')&$orderby=name%20asc,ID%20asc&$inlinecount=allpages";
@@ -232,15 +259,14 @@ void testBuildQueryStringWithStructuredQueryV4()
232259

233260
final StructuredQuery structuredQuery =
234261
StructuredQuery
235-
.onEntity(entityName, ODataProtocol.V4)
262+
.onEntity(entityName, V4)
236263
.filter(FieldReference.of("philosphy").equalTo("Yin & Yang"))
237264
.orderBy(OrderExpression.of("name", Order.ASC).and("ID", Order.ASC))
238265
.withInlineCount();
239266

240267
final String encodedQuery = structuredQuery.getEncodedQueryString();
241268

242-
final URI relativeUri =
243-
new ODataRequestRead(servicePath, entityName, encodedQuery, ODataProtocol.V4).getRelativeUri();
269+
final URI relativeUri = new ODataRequestRead(servicePath, entityName, encodedQuery, V4).getRelativeUri();
244270

245271
final String expectedUri =
246272
"/odata/v4/Service/Authors?$filter=(philosphy%20eq%20'Yin%20%26%20Yang')&$orderby=name%20asc,ID%20asc&$count=true";
@@ -257,7 +283,7 @@ void testCustomParameterswithStructuredQuery()
257283

258284
final StructuredQuery structuredQuery =
259285
StructuredQuery
260-
.onEntity(entityName, ODataProtocol.V4)
286+
.onEntity(entityName, V4)
261287
.filter(FieldReference.of("philosophy").equalTo("Yin & Yang"))
262288
.withCustomParameter(customKey, customValue);
263289

@@ -270,15 +296,15 @@ void testCustomParameterswithStructuredQuery()
270296
@Test
271297
void testCustomParametersOnNestedQuery()
272298
{
273-
final StructuredQuery query = StructuredQuery.asNestedQueryOnProperty("name", ODataProtocol.V4);
299+
final StructuredQuery query = StructuredQuery.asNestedQueryOnProperty("name", V4);
274300

275301
assertThatThrownBy(() -> query.withCustomParameter("key", "value")).isInstanceOf(IllegalStateException.class);
276302
}
277303

278304
@Test
279305
void testCustomParametersWithEmptyKey()
280306
{
281-
final StructuredQuery query = StructuredQuery.onEntity("name", ODataProtocol.V4);
307+
final StructuredQuery query = StructuredQuery.onEntity("name", V4);
282308

283309
assertThatThrownBy(() -> query.withCustomParameter("", "value")).isInstanceOf(IllegalArgumentException.class);
284310
}
@@ -288,17 +314,13 @@ void testConstructorWithStructuredQuery()
288314
{
289315
final StructuredQuery structuredQuery =
290316
StructuredQuery
291-
.onEntity("Authors", ODataProtocol.V4)
317+
.onEntity("Authors", V4)
292318
.filter(FieldReference.of("philosphy").equalTo("Yin & Yang"))
293319
.orderBy(OrderExpression.of("name", Order.ASC).and("ID", Order.ASC))
294320
.withInlineCount();
295321

296322
final ODataRequestRead expected =
297-
new ODataRequestRead(
298-
"/some/service/path",
299-
"Authors",
300-
structuredQuery.getEncodedQueryString(),
301-
ODataProtocol.V4);
323+
new ODataRequestRead("/some/service/path", "Authors", structuredQuery.getEncodedQueryString(), V4);
302324

303325
final ODataRequestRead actual =
304326
new ODataRequestRead("/some/service/path", new ODataResourcePath(), structuredQuery);
@@ -310,7 +332,7 @@ void testConstructorWithStructuredQuery()
310332
@Test
311333
void testConstructorWithStructuredQueryDoesNotMutateResourcePath()
312334
{
313-
final StructuredQuery structuredQuery = StructuredQuery.onEntity("Authors", ODataProtocol.V4).withInlineCount();
335+
final StructuredQuery structuredQuery = StructuredQuery.onEntity("Authors", V4).withInlineCount();
314336
final ODataResourcePath resourcePath = ODataResourcePath.of("Authors");
315337

316338
new ODataRequestRead("/some/service/path", resourcePath, structuredQuery);

release_notes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
### 📈 Improvements
1919

20-
-
20+
- (Generic OData Client) Allow for parameters in OData v4 expand sub-queries.
2121

2222
### 🐛 Fixed Issues
2323

0 commit comments

Comments
 (0)