diff --git a/cloudplatform/connectivity-apache-httpclient5/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/ApacheHttpClient5FactoryBuilder.java b/cloudplatform/connectivity-apache-httpclient5/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/ApacheHttpClient5FactoryBuilder.java index 9f07e5f671..527249bb74 100644 --- a/cloudplatform/connectivity-apache-httpclient5/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/ApacheHttpClient5FactoryBuilder.java +++ b/cloudplatform/connectivity-apache-httpclient5/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/ApacheHttpClient5FactoryBuilder.java @@ -20,7 +20,6 @@ public class ApacheHttpClient5FactoryBuilder private TlsUpgrade tlsUpgrade = TlsUpgrade.AUTOMATIC; private int maxConnectionsTotal = DefaultApacheHttpClient5Factory.DEFAULT_MAX_CONNECTIONS_TOTAL; private int maxConnectionsPerRoute = DefaultApacheHttpClient5Factory.DEFAULT_MAX_CONNECTIONS_PER_ROUTE; - private boolean csrfTokenInterceptorEnabled = false; /** * Enum to control the automatic TLS upgrade feature for insecure connections. @@ -146,27 +145,6 @@ public ApacheHttpClient5FactoryBuilder maxConnectionsPerRoute( final int maxConn return this; } - /** - * Enables the {@link CsrfTokenInterceptor} on {@link HttpClient} instances created by the to-be-built - * {@link ApacheHttpClient5Factory}. - *

- * When enabled, the interceptor automatically fetches a CSRF token via a HEAD request before every mutating HTTP - * request (POST, PUT, PATCH, DELETE) that does not already carry an {@code x-csrf-token} header. This is required - * when communicating with OData services that enforce CSRF protection. - *

- * By default, the CSRF token interceptor is disabled. Enable it when the built client will be used to call - * OData services. For general-purpose HTTP clients or REST/OpenAPI services that do not require CSRF protection, - * leave this disabled to avoid unnecessary HEAD requests. - * - * @return This builder. - */ - @Nonnull - public ApacheHttpClient5FactoryBuilder withCsrfTokenInterceptor() - { - this.csrfTokenInterceptorEnabled = true; - return this; - } - /** * Builds a new {@link ApacheHttpClient5Factory} instance with the previously configured parameters. * @@ -180,7 +158,6 @@ public ApacheHttpClient5Factory build() maxConnectionsTotal, maxConnectionsPerRoute, null, - tlsUpgrade, - csrfTokenInterceptorEnabled); + tlsUpgrade); } } diff --git a/cloudplatform/connectivity-apache-httpclient5/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5Factory.java b/cloudplatform/connectivity-apache-httpclient5/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5Factory.java index 6c976f4f86..7b7c0350e3 100644 --- a/cloudplatform/connectivity-apache-httpclient5/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5Factory.java +++ b/cloudplatform/connectivity-apache-httpclient5/src/main/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5Factory.java @@ -59,22 +59,18 @@ class DefaultApacheHttpClient5Factory implements ApacheHttpClient5Factory @Nonnull private final ApacheHttpClient5FactoryBuilder.TlsUpgrade tlsUpgrade; - private final boolean csrfTokenInterceptorEnabled; - DefaultApacheHttpClient5Factory( @Nonnull final Duration timeout, final int maxConnectionsTotal, final int maxConnectionsPerRoute, @Nullable final HttpRequestInterceptor requestInterceptor, - @Nonnull final ApacheHttpClient5FactoryBuilder.TlsUpgrade tlsUpgrade, - final boolean csrfTokenInterceptorEnabled ) + @Nonnull final ApacheHttpClient5FactoryBuilder.TlsUpgrade tlsUpgrade ) { this.timeout = toTimeout(timeout); this.maxConnectionsTotal = maxConnectionsTotal; this.maxConnectionsPerRoute = maxConnectionsPerRoute; this.requestInterceptor = requestInterceptor; this.tlsUpgrade = tlsUpgrade; - this.csrfTokenInterceptorEnabled = csrfTokenInterceptorEnabled; } @Nonnull @@ -109,16 +105,12 @@ private CloseableHttpClient buildHttpClient( builder.addRequestInterceptorFirst(requestInterceptor); } - if( csrfTokenInterceptorEnabled ) { - final AtomicReference holder = new AtomicReference<>(); - builder - .addRequestInterceptorLast( - ( req, entity, ctx ) -> new CsrfTokenInterceptor(holder.get()).process(req, entity, ctx)); - holder.set(builder.build()); - return holder.get(); - } - - return builder.build(); + final AtomicReference holder = new AtomicReference<>(); + builder + .addRequestInterceptorLast( + ( req, entity, ctx ) -> new CsrfTokenInterceptor(holder.get()).process(req, entity, ctx)); + holder.set(builder.build()); + return holder.get(); } @Nonnull diff --git a/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/ApacheHttpClient5FactoryBuilderTest.java b/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/ApacheHttpClient5FactoryBuilderTest.java index 3bc7e04957..de5f6a7523 100644 --- a/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/ApacheHttpClient5FactoryBuilderTest.java +++ b/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/ApacheHttpClient5FactoryBuilderTest.java @@ -1,31 +1,11 @@ package com.sap.cloud.sdk.cloudplatform.connectivity; -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.assertj.core.api.Assertions.assertThatNoException; -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.client5.http.classic.methods.HttpPost; import org.junit.jupiter.api.Test; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; - -import lombok.SneakyThrows; - -@WireMockTest class ApacheHttpClient5FactoryBuilderTest { - private static final String SERVICE_PATH = "/service/"; - private static final String RESOURCE_PATH = SERVICE_PATH + "Entity"; - private static final String CSRF_TOKEN = "test-token"; @Test void testBuilderContainsOptionalParametersOnly() @@ -33,50 +13,4 @@ void testBuilderContainsOptionalParametersOnly() // make sure we can build a new factory instance without supplying any parameters assertThatNoException().isThrownBy(() -> new ApacheHttpClient5FactoryBuilder().build()); } - - @Test - @SneakyThrows - void csrfInterceptorIsDisabledByDefault( final WireMockRuntimeInfo wm ) - { - wm.getWireMock().register(post(urlEqualTo(RESOURCE_PATH)).willReturn(noContent())); - - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - final HttpClient client = new ApacheHttpClient5FactoryBuilder().build().createHttpClient(destination); - - client.execute(new HttpPost(RESOURCE_PATH), r -> null); - - wm.getWireMock().verifyThat(1, postRequestedFor(urlEqualTo(RESOURCE_PATH))); - wm.getWireMock().verifyThat(0, headRequestedFor(anyUrl())); - } - - @Test - @SneakyThrows - void csrfInterceptorIsEnabledWhenOptedIn( final WireMockRuntimeInfo wm ) - { - wm - .getWireMock() - .register( - head(urlEqualTo(SERVICE_PATH)) - .willReturn(ok().withHeader(CsrfTokenInterceptor.X_CSRF_TOKEN_HEADER_KEY, CSRF_TOKEN))); - wm.getWireMock().register(post(urlEqualTo(RESOURCE_PATH)).willReturn(noContent())); - - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - final HttpClient client = - new ApacheHttpClient5FactoryBuilder().withCsrfTokenInterceptor().build().createHttpClient(destination); - - client.execute(new HttpPost(RESOURCE_PATH), r -> null); - - wm - .getWireMock() - .verifyThat( - 1, - headRequestedFor(urlEqualTo(SERVICE_PATH)) - .withHeader(CsrfTokenInterceptor.X_CSRF_TOKEN_HEADER_KEY, equalTo("fetch"))); - wm - .getWireMock() - .verifyThat( - 1, - postRequestedFor(urlEqualTo(RESOURCE_PATH)) - .withHeader(CsrfTokenInterceptor.X_CSRF_TOKEN_HEADER_KEY, equalTo(CSRF_TOKEN))); - } } diff --git a/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5CacheTest.java b/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5CacheTest.java index a1757e9033..64acf6973e 100644 --- a/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5CacheTest.java +++ b/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5CacheTest.java @@ -62,8 +62,7 @@ class DefaultApacheHttpClient5CacheTest DefaultApacheHttpClient5Factory.DEFAULT_MAX_CONNECTIONS_TOTAL, DefaultApacheHttpClient5Factory.DEFAULT_MAX_CONNECTIONS_PER_ROUTE, null, - ApacheHttpClient5FactoryBuilder.TlsUpgrade.AUTOMATIC, - false); + ApacheHttpClient5FactoryBuilder.TlsUpgrade.AUTOMATIC); private static final long NANOSECONDS_IN_MINUTE = 60_000_000_000L; private static final Duration TEN_MINUTES = Duration.ofMinutes(10L); diff --git a/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5FactoryTest.java b/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5FactoryTest.java index 408ad06699..80a9e6e01c 100644 --- a/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5FactoryTest.java +++ b/cloudplatform/connectivity-apache-httpclient5/src/test/java/com/sap/cloud/sdk/cloudplatform/connectivity/DefaultApacheHttpClient5FactoryTest.java @@ -86,8 +86,7 @@ void setup() MAX_CONNECTIONS, MAX_CONNECTIONS_PER_ROUTE, requestInterceptor, - AUTOMATIC, - false); + AUTOMATIC); } @Test @@ -102,8 +101,7 @@ void testHttpClientUsesTimeout() MAX_CONNECTIONS, MAX_CONNECTIONS_PER_ROUTE, requestInterceptor, - AUTOMATIC, - false); + AUTOMATIC); final ApacheHttpClient5Factory factoryWithEnoughTimeout = new DefaultApacheHttpClient5Factory( @@ -111,8 +109,7 @@ void testHttpClientUsesTimeout() MAX_CONNECTIONS, MAX_CONNECTIONS_PER_ROUTE, requestInterceptor, - AUTOMATIC, - false); + AUTOMATIC); final ClassicHttpRequest request = new HttpGet(WIRE_MOCK_SERVER.url("/timeout")); @@ -141,8 +138,7 @@ void testHttpClientUsesMaxConnections() 1, MAX_CONNECTIONS_PER_ROUTE, requestInterceptor, - AUTOMATIC, - false); + AUTOMATIC); final HttpClient client = sut.createHttpClient(); final ClassicHttpRequest firstRequest = new HttpGet(WIRE_MOCK_SERVER.url("/max-connections-1")); @@ -165,8 +161,7 @@ void testHttpClientUsesMaxConnectionsPerRoute() MAX_CONNECTIONS, 1, requestInterceptor, - AUTOMATIC, - false); + AUTOMATIC); final ClassicHttpRequest firstRequest = new HttpGet(WIRE_MOCK_SERVER.url("/max-connections-per-route")); final ClassicHttpRequest secondRequest = new HttpGet(SECOND_WIRE_MOCK_SERVER.url("/max-connections-per-route")); diff --git a/datamodel/odata-client-apache-httpclient5/pom.xml b/datamodel/odata-client-apache-httpclient5/pom.xml index d0ab2a931e..1f38f558e4 100644 --- a/datamodel/odata-client-apache-httpclient5/pom.xml +++ b/datamodel/odata-client-apache-httpclient5/pom.xml @@ -39,10 +39,6 @@ com.sap.cloud.sdk.cloudplatform connectivity-apache-httpclient5 - - com.sap.cloud.sdk.cloudplatform - cloudplatform-connectivity - io.vavr vavr @@ -83,6 +79,11 @@ cloudplatform-core test + + com.sap.cloud.sdk.cloudplatform + cloudplatform-connectivity + test + org.junit.jupiter junit-jupiter-api diff --git a/datamodel/odata-client-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/client/ODataApacheHttpClient5Accessor.java b/datamodel/odata-client-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/client/ODataApacheHttpClient5Accessor.java deleted file mode 100644 index 26de386d86..0000000000 --- a/datamodel/odata-client-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/client/ODataApacheHttpClient5Accessor.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.client; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Cache; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5CacheBuilder; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Factory; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5FactoryBuilder; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestinationProperties; -import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException; -import com.sap.cloud.sdk.cloudplatform.connectivity.exception.HttpClientInstantiationException; - -import io.vavr.control.Try; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -/** - * Accessor for {@link HttpClient} instances suitable for OData requests. - *

- * Unlike the general-purpose {@link com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor}, the - * clients returned by {@link #getHttpClient(HttpDestinationProperties)} have a CSRF token interceptor enabled (see - * {@link com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5FactoryBuilder#withCsrfTokenInterceptor()}) by - * default. The interceptor automatically fetches a CSRF token via a HEAD request before every mutating HTTP request - * (POST, PUT, PATCH, DELETE) that does not already carry an {@code x-csrf-token} header, which is required for OData - * services that enforce CSRF protection. - *

- * For OData requests that must opt out of CSRF token handling (e.g. against services that do not support it), use - * {@link #getHttpClientWithoutCsrf(HttpDestinationProperties)}, which returns a client without the interceptor. - *

- * For non-OData use cases (REST, OpenAPI) use - * {@link com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor} instead to avoid unnecessary CSRF HEAD - * requests. - * - * @since 5.35.0 - */ -@NoArgsConstructor( access = AccessLevel.PRIVATE ) -public final class ODataApacheHttpClient5Accessor -{ - /** - * Cache and factory backing {@link #getHttpClient(HttpDestinationProperties)}. The factory has the CSRF token - * interceptor enabled by default and can be replaced via {@link #setHttpClientFactory(ApacheHttpClient5Factory)}, - * e.g. to inject a custom or mocked client. - */ - @Nonnull - private static ApacheHttpClient5Cache httpClientCache = newDefaultCache(); - - @Nonnull - private static ApacheHttpClient5Factory httpClientFactory = newDefaultCsrfFactory(); - - /** - * Dedicated cache and factory backing {@link #getHttpClientWithoutCsrf(HttpDestinationProperties)}. These are kept - * separate from the CSRF-enabled cache above because the cache is keyed by destination only: sharing a single cache - * would return a CSRF-enabled client where a CSRF-disabled one was requested (and vice versa) for the same - * destination. - */ - @Nonnull - private static final ApacheHttpClient5Cache NO_CSRF_CACHE = newDefaultCache(); - - @Nonnull - private static final ApacheHttpClient5Factory NO_CSRF_FACTORY = new ApacheHttpClient5FactoryBuilder().build(); - - /** - * Sets the {@link ApacheHttpClient5Factory} used by {@link #getHttpClient(HttpDestinationProperties)}. - *

- * CAUTION: This factory is accessed concurrently. Therefore, you have to make sure that you do not - * introduce any concurrency issues when changing the factory. Furthermore, be aware that setting a custom factory - * will affect all consumers of the {@link ODataApacheHttpClient5Accessor} within the application. - * - * @param httpClientFactory - * The {@link ApacheHttpClient5Factory} instance to be used. Use {@code null} to reset the factory to the - * default CSRF-enabled factory. - */ - public static void setHttpClientFactory( @Nullable final ApacheHttpClient5Factory httpClientFactory ) - { - ODataApacheHttpClient5Accessor.httpClientFactory = - httpClientFactory == null ? newDefaultCsrfFactory() : httpClientFactory; - } - - /** - * Sets the {@link ApacheHttpClient5Cache} used by {@link #getHttpClient(HttpDestinationProperties)}. - *

- * CAUTION: This cache is accessed concurrently. Therefore, you have to make sure that you do not - * introduce any concurrency issues when changing the cache. Furthermore, be aware that setting a custom cache will - * affect all consumers of the {@link ODataApacheHttpClient5Accessor} within the application. - * - * @param httpClientCache - * The {@link ApacheHttpClient5Cache} instance to be used. Use {@code null} to reset the cache. - */ - public static void setHttpClientCache( @Nullable final ApacheHttpClient5Cache httpClientCache ) - { - ODataApacheHttpClient5Accessor.httpClientCache = httpClientCache == null ? newDefaultCache() : httpClientCache; - } - - @Nonnull - private static ApacheHttpClient5Cache newDefaultCache() - { - return new ApacheHttpClient5CacheBuilder().build(); - } - - @Nonnull - private static ApacheHttpClient5Factory newDefaultCsrfFactory() - { - return new ApacheHttpClient5FactoryBuilder().withCsrfTokenInterceptor().build(); - } - - /** - * Returns an {@link HttpClient} for the given destination with the CSRF token interceptor enabled. The instance may - * be cached. - * - * @param destination - * The destination to get the {@link HttpClient} for. - * @return An {@link HttpClient} configured for OData communication with the given destination. - * @throws DestinationAccessException - * If there is an issue accessing the destination. - * @throws HttpClientInstantiationException - * If there is an issue creating the {@link HttpClient}. - */ - @Nonnull - public static HttpClient getHttpClient( @Nonnull final HttpDestinationProperties destination ) - throws DestinationAccessException, - HttpClientInstantiationException - { - return getHttpClient((Destination) destination); - } - - /** - * Returns an {@link HttpClient} for the given destination with the CSRF token interceptor enabled. The instance may - * be cached. - * - * @param destination - * The destination to get the {@link HttpClient} for. - * @return An {@link HttpClient} configured for OData communication with the given destination. - * @throws DestinationAccessException - * If there is an issue accessing the destination. - * @throws HttpClientInstantiationException - * If there is an issue creating the {@link HttpClient}. - */ - @Nonnull - public static HttpClient getHttpClient( @Nonnull final Destination destination ) - throws DestinationAccessException, - HttpClientInstantiationException - { - return unwrap(tryGetHttpClient(destination, httpClientCache, httpClientFactory)); - } - - /** - * Returns an {@link HttpClient} for the given destination without the CSRF token interceptor. Use - * this for OData requests that must opt out of CSRF token handling. The instance may be cached. - * - * @param destination - * The destination to get the {@link HttpClient} for. - * @return An {@link HttpClient} for OData communication with the given destination, without CSRF token handling. - * @throws DestinationAccessException - * If there is an issue accessing the destination. - * @throws HttpClientInstantiationException - * If there is an issue creating the {@link HttpClient}. - */ - @Nonnull - public static HttpClient getHttpClientWithoutCsrf( @Nonnull final HttpDestinationProperties destination ) - throws DestinationAccessException, - HttpClientInstantiationException - { - return getHttpClientWithoutCsrf((Destination) destination); - } - - /** - * Returns an {@link HttpClient} for the given destination without the CSRF token interceptor. Use - * this for OData requests that must opt out of CSRF token handling. The instance may be cached. - * - * @param destination - * The destination to get the {@link HttpClient} for. - * @return An {@link HttpClient} for OData communication with the given destination, without CSRF token handling. - * @throws DestinationAccessException - * If there is an issue accessing the destination. - * @throws HttpClientInstantiationException - * If there is an issue creating the {@link HttpClient}. - */ - @Nonnull - public static HttpClient getHttpClientWithoutCsrf( @Nonnull final Destination destination ) - throws DestinationAccessException, - HttpClientInstantiationException - { - return unwrap(tryGetHttpClient(destination, NO_CSRF_CACHE, NO_CSRF_FACTORY)); - } - - @Nonnull - private static Try tryGetHttpClient( - @Nonnull final Destination destination, - @Nonnull final ApacheHttpClient5Cache cache, - @Nonnull final ApacheHttpClient5Factory factory ) - { - if( !destination.isHttp() ) { - return Try.failure(new DestinationAccessException("The given destination is not an HTTP destination.")); - } - return cache.tryGetHttpClient(destination.asHttp(), factory); - } - - @Nonnull - private static HttpClient unwrap( @Nonnull final Try httpClient ) - { - return httpClient.getOrElseThrow(failure -> { - if( failure instanceof DestinationAccessException ) { - throw (DestinationAccessException) failure; - } else if( failure instanceof HttpClientInstantiationException ) { - throw (HttpClientInstantiationException) failure; - } else { - throw new HttpClientInstantiationException("Failed to get HttpClient for destination.", failure); - } - }); - } -} diff --git a/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/ODataApacheHttpClient5AccessorTest.java b/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/ODataApacheHttpClient5AccessorTest.java deleted file mode 100644 index 0319d1adfa..0000000000 --- a/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/ODataApacheHttpClient5AccessorTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.client; - -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.assertj.core.api.Assertions.assertThat; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestAction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResult; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -@WireMockTest -class ODataApacheHttpClient5AccessorTest -{ - private static final String SERVICE_PATH = "/service/"; - private static final String ENTITY_SET = "Entities"; - private static final String ACTION_NAME = "TestAction"; - private static final String CSRF_TOKEN = "test-csrf-token"; - private static final String X_CSRF_TOKEN = "x-csrf-token"; - - private HttpClient client; - - @BeforeEach - void setup( final WireMockRuntimeInfo wm ) - { - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - client = ODataApacheHttpClient5Accessor.getHttpClient(destination); - } - - @Test - void csrfTokenIsFetchedForMutatingODataRequests( final WireMockRuntimeInfo wm ) - { - wm - .getWireMock() - .register(head(urlPathEqualTo(SERVICE_PATH)).willReturn(ok().withHeader(X_CSRF_TOKEN, CSRF_TOKEN))); - wm.getWireMock().register(post(urlPathEqualTo(SERVICE_PATH + ACTION_NAME)).willReturn(noContent())); - - final ODataRequestAction request = new ODataRequestAction(SERVICE_PATH, ACTION_NAME, null, ODataProtocol.V4); - final ODataRequestResult result = request.execute(client); - - assertThat(result).isNotNull(); - - wm - .getWireMock() - .verifyThat(1, headRequestedFor(urlPathEqualTo(SERVICE_PATH)).withHeader(X_CSRF_TOKEN, equalTo("fetch"))); - wm - .getWireMock() - .verifyThat( - 1, - postRequestedFor(urlPathEqualTo(SERVICE_PATH + ACTION_NAME)) - .withHeader(X_CSRF_TOKEN, equalTo(CSRF_TOKEN))); - } - - @Test - void noHeadRequestForReadRequests( final WireMockRuntimeInfo wm ) - { - wm.getWireMock().register(get(urlPathEqualTo(SERVICE_PATH + ENTITY_SET)).willReturn(okJson("{\"value\":[]}"))); - - final ODataRequestRead request = new ODataRequestRead(SERVICE_PATH, ENTITY_SET, "", ODataProtocol.V4); - final ODataRequestResultGeneric result = request.execute(client); - - assertThat(result).isNotNull(); - - wm.getWireMock().verifyThat(0, headRequestedFor(anyUrl())); - wm.getWireMock().verifyThat(1, getRequestedFor(urlPathEqualTo(SERVICE_PATH + ENTITY_SET))); - } -} diff --git a/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/request/ODataClientQueryBatchUnitTest.java b/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/request/ODataClientQueryBatchUnitTest.java index 143a207291..d7f6bbd34b 100644 --- a/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/request/ODataClientQueryBatchUnitTest.java +++ b/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/request/ODataClientQueryBatchUnitTest.java @@ -33,8 +33,9 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import com.google.common.io.Resources; +import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor; import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; +import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataConnectionException; import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataRequestException; @@ -55,7 +56,7 @@ class ODataClientQueryBatchUnitTest private static ODataRequestUpdate SAMPLE_REQUEST_UPDATE; private WireMockServer wireMockServer; - private DefaultHttpDestination destination; + private Destination destination; private final AtomicInteger uuidCounter = new AtomicInteger(0); private final Supplier uuidProvider = () -> new UUID(0, uuidCounter.incrementAndGet()); @@ -116,7 +117,7 @@ void testEmptyBatch() assertThat(request.getBatchRequestBody()).isEqualTo(requestBody); // check request execution - final HttpClient client = ODataApacheHttpClient5Accessor.getHttpClient(destination); + final HttpClient client = ApacheHttpClient5Accessor.getHttpClient(destination); wireMockServer.stubFor(post(urlPathEqualTo(SERVICE_PATH_BATCH)).willReturn(okJson("{}"))); final ODataRequestResult result = request.execute(client); @@ -145,7 +146,7 @@ void testEmptyChangesetBatch() assertThat(request.getBatchRequestBody()).isEqualTo(requestBody); // check request execution - final HttpClient client = ODataApacheHttpClient5Accessor.getHttpClient(destination); + final HttpClient client = ApacheHttpClient5Accessor.getHttpClient(destination); wireMockServer.stubFor(post(urlPathEqualTo(SERVICE_PATH_BATCH)).willReturn(okJson("{}"))); final ODataRequestResult result = request.execute(client); @@ -174,7 +175,7 @@ void testReadOnlyBatch() assertThat(request.getBatchRequestBody()).isEqualTo(requestBody); // check request execution - final HttpClient client = ODataApacheHttpClient5Accessor.getHttpClient(destination); + final HttpClient client = ApacheHttpClient5Accessor.getHttpClient(destination); wireMockServer.stubFor(post(urlPathEqualTo(SERVICE_PATH_BATCH)).willReturn(okJson("{}"))); final ODataRequestResult result = request.execute(client); @@ -204,7 +205,7 @@ void testReadOnlyBatchForceConnectionLeaks() assertThat(request.getBatchRequestBody()).isEqualTo(requestBody); // check request execution - final HttpClient client = ODataApacheHttpClient5Accessor.getHttpClient(destination); + final HttpClient client = ApacheHttpClient5Accessor.getHttpClient(destination); wireMockServer.stubFor(post(urlPathEqualTo(SERVICE_PATH_BATCH)).willReturn(okJson("{}"))); try { @@ -242,7 +243,7 @@ void testCombinedBatch() assertThat(request.getBatchRequestBody()).isEqualTo(requestBody); // check request execution - final HttpClient client = ODataApacheHttpClient5Accessor.getHttpClient(destination); + final HttpClient client = ApacheHttpClient5Accessor.getHttpClient(destination); wireMockServer.stubFor(post(urlPathEqualTo(SERVICE_PATH_BATCH)).willReturn(okJson("{}"))); final ODataRequestResult result = request.execute(client); diff --git a/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/request/ODataRequestActionTest.java b/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/request/ODataRequestActionTest.java index c99189a622..c366e38b71 100644 --- a/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/request/ODataRequestActionTest.java +++ b/datamodel/odata-client-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/client/request/ODataRequestActionTest.java @@ -22,8 +22,9 @@ import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.google.gson.GsonBuilder; +import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor; import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; +import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; @@ -43,8 +44,8 @@ class ODataRequestActionTest @BeforeEach void setup() { - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wireMockServer.baseUrl()).build(); - client = ODataApacheHttpClient5Accessor.getHttpClient(destination); + final Destination destination = DefaultHttpDestination.builder(wireMockServer.baseUrl()).build(); + client = ApacheHttpClient5Accessor.getHttpClient(destination); } @Test diff --git a/datamodel/odata-core-apache-httpclient5/pom.xml b/datamodel/odata-core-apache-httpclient5/pom.xml deleted file mode 100644 index bec91e2117..0000000000 --- a/datamodel/odata-core-apache-httpclient5/pom.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - 4.0.0 - - com.sap.cloud.sdk.datamodel - datamodel-parent - 5.35.0-SNAPSHOT - - odata-core-apache-httpclient5 - jar - Data Model - OData Services - Core (HttpClient 5) - OData Services data model (VDM) - core classes using Apache HttpClient 5. - https://sap.github.io/cloud-sdk/docs/java/getting-started - - SAP SE - https://www.sap.com - - - - The Apache Software License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - SAP - cloudsdk@sap.com - SAP SE - https://www.sap.com - - - - false - - - - com.sap.cloud.sdk.datamodel - odata-client-apache-httpclient5 - ${project.version} - - - com.sap.cloud.sdk.cloudplatform - cloudplatform-core - - - com.sap.cloud.sdk.cloudplatform - cloudplatform-connectivity - - - com.sap.cloud.sdk.datamodel - fluent-result - - - org.slf4j - slf4j-api - - - com.google.guava - guava - - - com.google.code.gson - gson - - - com.fasterxml.jackson.core - jackson-annotations - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - - - org.apache.httpcomponents.core5 - httpcore5 - - - org.apache.httpcomponents.client5 - httpclient5 - - - io.vavr - vavr - - - - org.projectlombok - lombok - provided - - - - com.sap.cloud.sdk.cloudplatform - connectivity-apache-httpclient5 - test - - - org.assertj - assertj-core - test - - - org.mockito - mockito-core - test - - - org.wiremock - wiremock - test - - - org.junit.jupiter - junit-jupiter-api - test - - - org.junit.jupiter - junit-jupiter-params - test - - - diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/adapter/ODataNumberSerializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/adapter/ODataNumberSerializer.java deleted file mode 100644 index 62efe10b08..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/adapter/ODataNumberSerializer.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.adapter; - -import java.lang.reflect.Type; -import java.math.BigDecimal; - -import javax.annotation.Nonnull; - -import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; - -/** - * GSON serializer that transforms numbers to their JSON representation according to the OData V2 standard. Meant for - * internal use only. - */ -public class ODataNumberSerializer implements JsonSerializer -{ - @Override - @Nonnull - public JsonElement serialize( - @Nonnull final Number src, - @Nonnull final Type typeOfSrc, - @Nonnull final JsonSerializationContext context ) - { - /* - Short is used both for Edm.Byte and Edm.Int16. - Edm.Byte should be a string but Edm.Int16 should be a number. - But we can't differentiate between that here because we only know it's a Short. - So we serialize to the plain number because this worked in the past. - */ - if( typeOfSrc == Integer.class || typeOfSrc == Short.class ) { - return new JsonPrimitive(src); - } else if( typeOfSrc == BigDecimal.class ) { - return new JsonPrimitive(((BigDecimal) src).toPlainString()); - } else { - return new JsonPrimitive(src.toString()); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/CollectionValuedFluentHelperFunction.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/CollectionValuedFluentHelperFunction.java deleted file mode 100644 index 572b3d661f..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/CollectionValuedFluentHelperFunction.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; - -/** - * Representation of any OData function import as a fluent class for further configuring the request and - * {@link #executeRequest(Destination) executing} it.This is specifically for functions that return either a collection - * of primitive values or entities - * - * @param - * The fluent helper type. - * @param - * The type of the object this OData request operates on, if any. - * @param - * The type of the result entity, if any. - */ -public abstract class CollectionValuedFluentHelperFunction - extends - FluentHelperFunction -{ - - /** - * Instantiates this fluent helper using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - */ - public CollectionValuedFluentHelperFunction( @Nonnull final String servicePath ) - { - super(servicePath); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntityField.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntityField.java deleted file mode 100644 index 27a63b2432..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntityField.java +++ /dev/null @@ -1,219 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldUntyped; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionString; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueString; -import com.sap.cloud.sdk.typeconverter.TypeConverter; - -import lombok.EqualsAndHashCode; -import lombok.Getter; - -/** - * Template class to represent entity fields. Instances of this object are used in query modifier methods of the entity - * fluent helpers. Contains methods to compare a field's value with a provided value. - *

- * Use the constants declared in each entity inner class. Instantiating directly requires knowing the underlying OData - * field names, so use the constructor with caution. - * - * @param - * VdmObject that the field belongs to - * @param - * Field type - */ -@EqualsAndHashCode -public class EntityField implements EntitySelectable -{ - @Nonnull - @Getter - private final String fieldName; - - @Nullable - @Getter - private final TypeConverter typeConverter; - - @Nonnull - private final FieldUntyped fieldUntyped; - - /** - * Use the constants declared in each entity inner class. Instantiating directly requires knowing the underlying - * OData field names, so use with caution. - * - * @param fieldName - * OData field name. Must match the field returned by the underlying OData service. - */ - public EntityField( @Nonnull final String fieldName ) - { - this(fieldName, null); - } - - /** - * Use the constants declared in each entity inner class. Instantiating directly requires knowing the underlying - * OData field names, so use with caution. - *

- * When creating instances for custom fields, this constructor can be used to add a type converter that will be - * automatically used by the respective entity when getting or setting custom fields. - * - * @param fieldName - * OData field name. Must match the field returned by the underlying OData service. - * @param typeConverter - * An implementation of a TypeConverter. The first type must match FieldT, the second type must match the - * type Olingo returns. - */ - public EntityField( @Nonnull final String fieldName, @Nullable final TypeConverter typeConverter ) - { - this.fieldName = fieldName; - this.typeConverter = typeConverter; - fieldUntyped = FieldReference.of(fieldName); - } - - /** - * Equals-null expression fluent helper. - * - * @return Fluent helper that represents a field == null expression. - */ - @Nonnull - public ExpressionFluentHelper eqNull() - { - return new ExpressionFluentHelper<>(fieldUntyped.equalToNull()); - } - - /** - * Equals expression fluent helper. - * - * @param value - * Field value to compare with. - * - * @return Fluent helper that represents a field == value expression. - */ - @Nonnull - public ExpressionFluentHelper eq( @Nullable final FieldT value ) - { - return new ExpressionFluentHelper<>(fieldUntyped.equalTo(value)); - } - - /** - * Not equals-null expression fluent helper. - * - * @return Fluent helper that represents a field != null expression. - */ - @Nonnull - public ExpressionFluentHelper neNull() - { - return new ExpressionFluentHelper<>(fieldUntyped.notEqualToNull()); - } - - /** - * Not equals expression fluent helper. - * - * @param value - * Field value to compare with. - * - * @return Fluent helper that represents a field != value expression. - */ - @Nonnull - public ExpressionFluentHelper ne( @Nullable final FieldT value ) - { - return new ExpressionFluentHelper<>(fieldUntyped.notEqualTo(value)); - } - - /** - * Greater than expression fluent helper. - * - * @param value - * Field value to compare with. - * - * @return Fluent helper that represents a field > value expression. - */ - @Nonnull - public ExpressionFluentHelper gt( @Nullable final FieldT value ) - { - return new ExpressionFluentHelper<>(fieldUntyped.greaterThan(value)); - } - - /** - * Greater than or equals expression fluent helper. - * - * @param value - * Field value to compare with. - * - * @return Fluent helper that represents a field ≥ value expression. - */ - @Nonnull - public ExpressionFluentHelper ge( @Nullable final FieldT value ) - { - return new ExpressionFluentHelper<>(fieldUntyped.greaterThanEqual(value)); - } - - /** - * Less than expression fluent helper. - * - * @param value - * Field value to compare with. - * - * @return Fluent helper that represents a field < value expression. - */ - @Nonnull - public ExpressionFluentHelper lt( @Nullable final FieldT value ) - { - return new ExpressionFluentHelper<>(fieldUntyped.lessThan(value)); - } - - /** - * Less than or equals expression fluent helper. - * - * @param value - * Field value to compare with. - * - * @return Fluent helper that represents a field ≤ value expression. - */ - @Nonnull - public ExpressionFluentHelper le( @Nullable final FieldT value ) - { - return new ExpressionFluentHelper<>(fieldUntyped.lessThanEqual(value)); - } - - /** - * Expression fluent helper supporting the filter function "substringof". - * - * @param value - * value String value to apply the function on - * - * @return Fluent helper that represents a {@code substringof(value,field)} expression - */ - @Nonnull - public ExpressionFluentHelper substringOf( @Nonnull final String value ) - { - return new ExpressionFluentHelper<>( - FilterExpressionString.substringOf(ValueString.literal(value), fieldUntyped.asString())); - } - - /** - * Expression fluent helper supporting the filter function "endswith". - * - * @param value - * String value to apply the function on - * @return Fluent helper that represents a {@code endswith(field,value)} expression - */ - @Nonnull - public ExpressionFluentHelper endsWith( @Nonnull final String value ) - { - return new ExpressionFluentHelper<>(fieldUntyped.asString().endsWith(value)); - } - - /** - * Expression fluent helper supporting the filter function "startswith". - * - * @param value - * String value to apply the function on - * @return Fluent helper that represents a {@code startswith(field,value)} expression - */ - @Nonnull - public ExpressionFluentHelper startsWith( @Nonnull final String value ) - { - return new ExpressionFluentHelper<>(fieldUntyped.asString().startsWith(value)); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntityFieldAll.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntityFieldAll.java deleted file mode 100644 index a45d275c1d..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntityFieldAll.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.Collections; -import java.util.List; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/** - * OData selector for entities to select each and every field, i.e. star selector. Instances of this object are used in - * query modifier methods of the entity fluent helpers. Methods to compare with provided values are not supported. - * - * @param - * VdmObject that the field belongs to - */ -public class EntityFieldAll> implements EntitySelectable -{ - @Nonnull - @Override - public String getFieldName() - { - return "*"; - } - - @Nonnull - @Override - public List getSelections() - { - return Collections.singletonList("*"); - } - - @Nonnull - @Override - public String toString() - { - return "*"; - } - - @Override - public boolean equals( @Nullable final Object o ) - { - return o instanceof EntitySelectable && ((EntitySelectable) o).getSelections().equals(getSelections()); - } - - @Override - public int hashCode() - { - return getSelections().hashCode(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntityLink.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntityLink.java deleted file mode 100644 index f4232eb038..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntityLink.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.common.collect.Lists; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; - -/** - * Helper class for representing links (navigation properties) between entities. - * - * @param - * The type of this link. - * @param - * The type of the entity. - * @param - * The type of the subentity. - */ -@ToString -@EqualsAndHashCode -@RequiredArgsConstructor -public class EntityLink, EntityT extends VdmObject, SubEntityT extends VdmObject> - implements - EntitySelectable -{ - private static final String STAR_SELECTOR = "*"; - - private final List> descendants = new ArrayList<>(); - private final List> selectors = new ArrayList<>(); - - @Nonnull - @Getter - private final String fieldName; - - private EntityLink( - @Nonnull final EntityLink toCopy, - @Nullable final Iterable> addDescendants, - @Nullable final Iterable> addSelectors ) - { - this(toCopy.fieldName); - descendants.addAll(toCopy.descendants); - selectors.addAll(toCopy.selectors); - - if( addDescendants != null ) { - descendants.addAll(Lists.newArrayList(addDescendants)); - } - - if( addSelectors != null ) { - selectors.addAll(Lists.newArrayList(addSelectors)); - } - } - - /** - * Copy constructor. - * - * @param toCopy - * The link to copy. - */ - protected EntityLink( @Nonnull final EntityLink toCopy ) - { - this(toCopy, null, null); - } - - /** - * Used in combination with {@link FluentHelperRead#select(Object[]) FluentHelperRead.select} when expanding a - * navigation property to specify which fields of that navigation property to select, and which navigation - * properties of that navigation property to expand. - * - * @param selectors - * Array of fields to select and/or navigation properties to expand. - * @return Selector for {@link FluentHelperRead#select(Object[]) FluentHelperRead.select}. - */ - @SuppressWarnings( { "unchecked", "varargs" } ) - @SafeVarargs - @Nonnull - public final LinkT select( @Nonnull final EntitySelectable... selectors ) - { - final List> additionalDescendants = new ArrayList<>(); - final List> additionalSelectors = new ArrayList<>(); - - for( final EntitySelectable select : selectors ) { - if( select instanceof EntityLink ) { - additionalDescendants.add((EntityLink) select); - } else { - additionalSelectors.add(select); - } - } - - final EntityLink toTranslate = - new EntityLink<>(this, additionalDescendants, additionalSelectors); - - return translateLinkType(toTranslate); - } - - /* - * may not be not required, since getSelections() provide implicit expand definitions. - * E.g. select(to_BusinessPartnerRole/*) => expand(to_BusinessPartner) - */ - - /** - * Returns a list of expansions for this link. - * - * @return A list of expansions for this link. - */ - @Nonnull - public List getExpansions() - { - if( descendants.isEmpty() ) { - return Lists.newArrayList(getFieldName()); - } - - final List result = new ArrayList<>(); - for( final EntityLink descendant : descendants ) { - for( final String name : descendant.getExpansions() ) { - result.add(getFieldName() + "/" + name); - } - } - return result; - } - - @Nonnull - @Override - public List getSelections() - { - final List result = new ArrayList<>(); - if( selectors.isEmpty() && descendants.isEmpty() ) { - result.add(getFieldName() + "/" + STAR_SELECTOR); - } - for( final EntitySelectable field : selectors ) { - result.add(getFieldName() + "/" + field.getFieldName()); - } - - for( final EntityLink descendant : descendants ) { - for( final String name : descendant.getSelections() ) { - result.add(getFieldName() + "/" + name); - } - } - return result; - } - - /** - * Returns the given {@code link} in a type-safe manner. - * - * @param link - * The link to cast. - * @return The given {@code link} in a type-safe manner. - */ - @SuppressWarnings( "unchecked" ) - @Nonnull - protected LinkT translateLinkType( final EntityLink link ) - { - return (LinkT) link; - } - - /** - * Add a filter expression on a single navigation property. - * - * @param filterExpression - * The filter to apply. - * @return A new expression builder that includes the given filter. - */ - @Nonnull - protected ExpressionFluentHelper filterOnOneToOneLink( - @Nonnull final ExpressionFluentHelper filterExpression ) - { - final ValueBoolean exp = - ( protocol, prefixes ) -> getFieldName() - + "/" - + filterExpression.getDelegateExpressionWithoutOuterParentheses().getExpression(protocol, prefixes); - return new ExpressionFluentHelper<>(exp); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntitySelectable.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntitySelectable.java deleted file mode 100644 index 833acdcba1..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/EntitySelectable.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.List; - -import javax.annotation.Nonnull; - -import com.google.common.collect.Lists; - -/** - * Interface to enable management of OData entity selectors. This interface is used by - * {@link com.sap.cloud.sdk.datamodel.odata.helper.EntityField EntityField} and - * {@link com.sap.cloud.sdk.datamodel.odata.helper.EntityLink EntityLink}. - * - * @param - * The generic entity type. - */ -public interface EntitySelectable -{ - /** - * Get the field name of OData entity property. - * - * @return The field name - */ - @Nonnull - String getFieldName(); - - /** - * Get the select expression that represents the OData entity property. - * - * @return The serialized select terms. - */ - @Nonnull - default List getSelections() - { - return Lists.newArrayList(getFieldName()); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ExpressionFluentHelper.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ExpressionFluentHelper.java deleted file mode 100644 index 49655ee1c3..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ExpressionFluentHelper.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; - -import lombok.extern.slf4j.Slf4j; - -/** - * Template class that represents query expressions. Instances of this object are used in query modifier methods of the - * entity fluent helpers. - * - * Use either the expression methods from EntityField instances, or the logical operators - * {@link #and(ExpressionFluentHelper) and} and {@link #or(ExpressionFluentHelper) or} as methods in this class. - * Negation can be achieved by {@link #not() not}. Every logical operator creates and returns a new instance based on - * the original expression object. Instantiating objects from this class directly can cause undefined results. - * - * @see ExpressionFluentHelper#not(ExpressionFluentHelper) - * - * @param - * VdmObject that the expression is operating on. - */ -@Slf4j -public class ExpressionFluentHelper -{ - private final ValueBoolean delegateExpression; - - /** - * Creates a new helper based on an arbitrary, untyped filter expression. - *

- * Instances of this class can be used to pass an unchecked {@link ValueBoolean} to fluent helpers. This approach - * discards type safety and is generally discouraged. - * - * @param delegateExpression - * The expression to delegate to. - */ - public ExpressionFluentHelper( @Nonnull final ValueBoolean delegateExpression ) - { - this.delegateExpression = delegateExpression; - } - - /** - *

- * Boolean OR expression fluent helper. - *

- *

- * Please note:
- * Filter expressions chained together by logical operators are interpreted in the same order as their corresponding - * methods are called. Since the Java language evaluates method calls from left to right, the fluent API design is - * following the same principle. The implicit precedence is following the method invocation and not the underlying, - * logical operators: - * - *

-     *     A.or(B).and(C) <=> (A.or(B)).and(C)
-     * 
- * - *

- *

- * Recommendation:
- * Incorporate parentheses or introduce variables to reflect combined expressions: - * - *

-     *     var AorB = A.or(B)
-     *     AorB.and(C)
-     * 
- * - *

- * - * - * @param disjunctExpression - * Other expression to combine with. - * - * @return Fluent helper that represents a ((this) || other) expression. - */ - @Nonnull - public ExpressionFluentHelper or( @Nonnull final ExpressionFluentHelper disjunctExpression ) - { - return new ExpressionFluentHelper<>(delegateExpression.or(disjunctExpression.delegateExpression)); - } - - /** - *

- * Boolean AND expression fluent helper. - *

- *

- * Please note:
- * Filter expressions chained together by logical operators are interpreted in the same order as their corresponding - * methods are called. Since the Java language evaluates method calls from left to right, the fluent API design is - * following the same principle. The implicit precedence is following the method invocation and not the underlying, - * logical operators: - * - *

-     *     A.or(B).and(C) <=> (A.or(B)).and(C)
-     * 
- * - *

- *

- * Recommendation:
- * Incorporate parentheses or introduce variables to reflect combined expressions: - * - *

-     *     var AorB = A.or(B)
-     *     AorB.and(C)
-     * 
- * - *

- * - * @param conjunctExpression - * Other expression to combine with. - * - * @return Fluent helper that represents a ((this) && other) expression. - */ - @Nonnull - public ExpressionFluentHelper and( @Nonnull final ExpressionFluentHelper conjunctExpression ) - { - return new ExpressionFluentHelper<>(delegateExpression.and(conjunctExpression.delegateExpression)); - } - - /** - * Boolean NOT expression fluent helper. - * - * @return Fluent helper that represents a not(this) expression. - */ - @Nonnull - public ExpressionFluentHelper not() - { - return new ExpressionFluentHelper<>(delegateExpression.not()); - } - - /** - * Boolean NOT expression fluent helper. - * - * @param - * The type argument for the returned {@link ExpressionFluentHelper} - * @param expression - * expression to be negated. - * - * @return Fluent helper that represents a not(expression) expression. - */ - @Nonnull - public static ExpressionFluentHelper not( @Nonnull final ExpressionFluentHelper expression ) - { - return expression.not(); - } - - @Nonnull - ValueBoolean getDelegateExpressionWithoutOuterParentheses() - { - return ( prefixes, protocol ) -> { - final String expression = delegateExpression.getExpression(prefixes, protocol); - return expression.startsWith("(") && expression.endsWith(")") - ? expression.substring(1, expression.length() - 1) - : expression; - }; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperBasic.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperBasic.java deleted file mode 100644 index 1fca5d85e8..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperBasic.java +++ /dev/null @@ -1,265 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.query.StructuredQuery; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataUriFactory; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of any OData request as a fluent interface for further configuring the request and - * {@link #executeRequest(Destination) executing} it. - * - * @param - * The fluent helper type. - * @param - * The type of the entity this OData request operates on, if any. - * @param - * The type of the result entity, if any. - */ -@Slf4j -public abstract class FluentHelperBasic implements FluentHelperExecutable -{ - @Nonnull - @Getter( AccessLevel.PROTECTED ) - private final String servicePath; - - /** - * The entity collection to send the OData requests to - */ - @Nullable - protected String entityCollection = null; - - /** - * A map containing the headers to be used for all requests that are part of this FluentHelper implementation. - */ - private final Map headers = new LinkedHashMap<>(); - - /** - * Whether the CSRF token retrieval should be skipped for the actual request of this FluentHelper implementation. - */ - private boolean skipCsrfTokenRetrieval = false; - - /** - * A map containing the custom query parameters to be used only for the actual request of this FluentHelper - * implementation. - */ - @Getter( AccessLevel.PROTECTED ) - private final Map parametersForRequestOnly = new LinkedHashMap<>(); - - /** - * Returns a class object of the type this fluent helper works with. - * - * @return A class object of the handled type. - */ - @Nonnull - protected abstract Class getEntityClass(); - - /** - * Instantiates this fluent helper using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityCollection - * The entity collection to direct the requests to. - */ - public FluentHelperBasic( @Nonnull final String servicePath, @Nullable final String entityCollection ) - { - this.servicePath = servicePath; - this.entityCollection = entityCollection; - } - - /** - * Returns the current fluent helper instance. - * - * @return The current fluent helper instance. - */ - @SuppressWarnings( "unchecked" ) - @Nonnull - protected FluentHelperT getThis() - { - return (FluentHelperT) this; - } - - @Nullable - @Override - public abstract ResultT executeRequest( @Nonnull final Destination destination ); - - /** - * Get all headers for explicit and implicit requests. - * - * @return a map containing the headers to be used for all requests that are part of this FluentHelper - * implementation. - */ - protected Map getHeaders() - { - return headers; - } - - /** - * Marks this fluent helper to skip CSRF token retrieval for its actual request, so the request is sent with an - * {@link HttpClient} that does not have the CSRF token interceptor enabled. - */ - protected void setSkipCsrfTokenRetrieval() - { - skipCsrfTokenRetrieval = true; - } - - /** - * Returns the {@link HttpClient} to be used for the actual request of this fluent helper. If CSRF token retrieval - * was disabled via {@code withoutCsrfToken()}, a client without the CSRF token interceptor is returned. - * - * @param destination - * The destination to get the {@link HttpClient} for. - * @return An {@link HttpClient} for the given destination. - */ - @Nonnull - protected HttpClient getHttpClient( @Nonnull final Destination destination ) - { - return skipCsrfTokenRetrieval - ? ODataApacheHttpClient5Accessor.getHttpClientWithoutCsrf(destination) - : ODataApacheHttpClient5Accessor.getHttpClient(destination); - } - - /** - * Gives the option to specify custom HTTP headers. The returned object allows to specify the requests the headers - * should be used in. - * - * @param key - * Name of the (first) desired HTTP header parameter. - * @param value - * Value of the (first) desired HTTP header parameter. - * - * @return A fluent helper to specify further headers and their intended usage. - */ - @Nonnull - public FluentHelperT withHeader( @Nonnull final String key, @Nullable final String value ) - { - headers.put(key, value); - return getThis(); - } - - /** - * Gives the option to specify a map of custom HTTP headers. The returned object allows to specify the requests the - * headers should be used in. - * - * @param map - * A map of HTTP header key/value pairs. - * @return A fluent helper to specify further headers and their intended usage. - */ - @Nonnull - public FluentHelperT withHeaders( @Nonnull final Map map ) - { - headers.putAll(map); - return getThis(); - } - - /** - * Gives the option to specify custom query parameters for the request. The passed parameter value will be encoded - * with percentage encoding. - * - *

- * Note: It is recommended to only use this function for query parameters which are not supported - * by the VDM by default. Using this function to bypass fluent helper method calls can lead to unsupported response - * handling. There is no contract on the order or priority of parameters added to the query. - *

- * - *

- * Example: Use the query option $search to reduce the result set, leaving only - * entities which match the specified search expression. This feature is supported in protocol OData v4. - * - *

-     * new DefaultBusinessPartnerService().getAllBusinessPartner().withQueryParameter("$search", "Köln OR Cologne")
-     * 
- *

- * - * @param key - * Name of the query parameter. - * @param value - * Unencoded value of the query parameter. - * - * @return The same fluent helper. - */ - @Nonnull - protected FluentHelperT withQueryParameter( @Nonnull final String key, @Nullable final String value ) - { - parametersForRequestOnly.put(key, value); - return getThis(); - } - - /** - * Query modifier to limit which field values of the entity get fetched and populated, and to specify which - * navigation properties to expand. If this method is never called, then all fields will be fetched and populated, - * and no navigation properties expanded. But if this method is called at least once, then only the specified fields - * will be fetched and populated. Calling this multiple times will combine the set(s) of fields and expansions of - * each call. - * - * @param fields - * Fields to select and/or navigation properties to expand. - * - * @param delegateSelect - * Handler to accept simple property names being put inside a delegate "select" query option. - * @param delegateExpand - * Handler to accept navigation properties being put inside a delegate "expand" query option. - * @return The same fluent helper with this query modifier applied. - */ - @Nonnull - FluentHelperT select( - @Nonnull final Iterable> fields, - @Nonnull final Consumer delegateSelect, - @Nonnull final Consumer delegateExpand ) - { - for( final EntitySelectable field : fields ) { - final List selections = field.getSelections(); - - // add items to selections - selections.forEach(delegateSelect); - - // add items to expansions - for( final String fieldName : selections ) { - final int lastSlash = fieldName.lastIndexOf("/"); - if( lastSlash > 0 ) { - final String expandString = fieldName.substring(0, lastSlash); - final StructuredQuery q = StructuredQuery.asNestedQueryOnProperty(expandString, ODataProtocol.V2); - delegateExpand.accept(q); - } - } - } - - return getThis(); - } - - /** - * Translate this OData v2 request into a OData request object extending {@link ODataRequestGeneric}. - * - * @return A protocol agnostic OData request instance. - */ - @Nonnull - public abstract ODataRequestGeneric toRequest(); - - @Nonnull - RequestT addHeadersAndCustomParameters( @Nonnull final RequestT request ) - { - getHeaders().forEach(request::addHeader); - - getParametersForRequestOnly() - .forEach(( key, value ) -> request.addQueryParameter(key, ODataUriFactory.encodeQuery(value))); - - return request; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperByKey.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperByKey.java deleted file mode 100644 index 0c3083472b..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperByKey.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.Arrays; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.query.StructuredQuery; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestReadByKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -/** - * Representation of an OData request to retrieve an entity by its key as a fluent interface for further configuring the - * request and {@link #executeRequest(Destination) executing} it. - * - * @param - * The fluent helper type. - * @param - * The type of the result entity. - * @param - * The type of the class that represents fields of the entity. - */ -public abstract class FluentHelperByKey, SelectableT> - extends - FluentHelperBasic -{ - private final StructuredQuery delegateQuery; - - /** - * Instantiates this fluent helper using the given service path and entity collection to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * - * @param entityCollection - * The entity collection to direct the requests to. - */ - public FluentHelperByKey( @Nonnull final String servicePath, @Nonnull final String entityCollection ) - { - super(servicePath, entityCollection); - delegateQuery = StructuredQuery.onEntity(entityCollection, ODataProtocol.V2); - } - - /** - * Getter for a map containing the OData name of key properties, each mapped to the value to search by. - * - * @return A name-value mapping for the OData key properties. - */ - @Nonnull - protected abstract Map getKey(); - - @Override - @Nonnull - public ODataRequestReadByKey toRequest() - { - final ODataEntityKey entityKey = ODataEntityKey.of(getKey(), ODataProtocol.V2); - final String queryString = delegateQuery.getEncodedQueryString(); - - final ODataRequestReadByKey request = - new ODataRequestReadByKey(getServicePath(), entityCollection, entityKey, queryString, ODataProtocol.V2); - - return super.addHeadersAndCustomParameters(request); - } - - @Override - @Nonnull - public FluentHelperT withQueryParameter( @Nonnull final String key, @Nullable final String value ) - { - return super.withQueryParameter(key, value); - } - - /** - * Query modifier to limit which field values of the entity get fetched & populated. If this method is never - * called, then all fields will be fetched & populated. But if this method is called at least once, then only - * the specified fields will be fetched & populated. Calling this multiple times will combine the set(s) of - * fields of each call. - * - * @param fields - * Array of fields to select. - * - * @return The same fluent helper with the provided fields selected. - */ - @SuppressWarnings( "unchecked" ) - @Nonnull - public FluentHelperT select( @Nonnull final SelectableT... fields ) - { - final Iterable> selectableFields = (Iterable>) Arrays.asList(fields); - return super.select(selectableFields, delegateQuery::select, delegateQuery::select); - } - - @Override - @Nonnull - public EntityT executeRequest( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - - final EntityT result = response.as(getEntityClass()); - - // use version identifier from header if present - response.getVersionIdentifierFromHeader().peek(result::setVersionIdentifier); - - result.attachToService(getServicePath(), destination); - return result; - } - - /** - * Activates CSRF token retrieval for this OData request. - * - * @return The same fluent helper that will now fetch a CSRF token. - * @deprecated CSRF token handling is now performed automatically by the underlying HTTP client. This method is a - * no-op retained only for source compatibility, as read requests never require a CSRF token. It is - * scheduled for removal. - */ - @Deprecated - @Nonnull - public FluentHelperT withCsrfToken() - { - return getThis(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperCount.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperCount.java deleted file mode 100644 index 5999300bc9..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperCount.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException; -import com.sap.cloud.sdk.cloudplatform.connectivity.exception.HttpClientInstantiationException; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCount; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; - -/** - * Representation of an OData query for count, as a fluent interface for further configuring the request and - * {@link #executeRequest(Destination) executing} it. - */ -@RequiredArgsConstructor( access = AccessLevel.PACKAGE ) -public class FluentHelperCount -{ - private final ODataRequestCount request; - - /** - * Executes the underlying query for count, using the stored values, plus any query modifiers that were previously - * called. - * - * @param destination - * The target system this request should be issued against. - * @return The number of tuples that match the criteria specified in the query. - * - * @throws DestinationAccessException - * If there is an issue accessing the - * {@link com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination}. - * @throws HttpClientInstantiationException - * If there is an issue creating the {@link HttpClient}. - * @throws com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException - * If the OData request execution failed. Please find the documentation for {@link ODataException} - * possible sub-types and error scenarios they can occur in. - */ - public long executeRequest( @Nonnull final Destination destination ) - { - final ODataRequestCount requestCount = toRequest(); - - final ODataRequestResultGeneric result = - requestCount.execute(ODataApacheHttpClient5Accessor.getHttpClient(destination)); - - return result.as(Long.class); - } - - /** - * Creates an instance of {@link ODataRequestCount}. - *

- * The following settings are used: - *

    - *
  • the endpoint URL
  • - *
  • the entity collection name
  • - *
  • the OData query
  • - *
- * - * @return A new count request with the given configuraiton. - */ - @Nonnull - public ODataRequestCount toRequest() - { - return request; - } - - /** - * Activates CSRF token retrieval for this OData request. - * - * @return The same fluent helper that will now fetch a CSRF token. - * @deprecated CSRF token handling is now performed automatically by the underlying HTTP client. This method is a - * no-op retained only for source compatibility, as read requests never require a CSRF token. It is - * scheduled for removal. - */ - @Deprecated - @Nonnull - public FluentHelperCount withCsrfToken() - { - return this; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperCreate.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperCreate.java deleted file mode 100644 index 71c353d489..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperCreate.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataSerializationException; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCreate; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import io.vavr.control.Option; -import io.vavr.control.Try; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an OData create request as a fluent interface for further configuring the request and - * {@link #executeRequest(Destination) executing} it. - * - * @param - * The fluent helper type. - * @param - * The type of the entity to create. - */ -@Slf4j -public abstract class FluentHelperCreate> - extends - FluentHelperModification -{ - private EntityLink, ?, EntityT> linkFromParentEntity; - private VdmEntity parentEntity; - - /** - * Instantiates this fluent helper using the given service path and entity collection to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * - * @param entityCollection - * The entity collection to direct the requests to. - */ - public FluentHelperCreate( @Nonnull final String servicePath, @Nonnull final String entityCollection ) - { - super(servicePath, entityCollection); - } - - /** - * Getter for the VDM representation of the entity to be created. - * - * @return The entity that should be created by calling the {@link #executeRequest(Destination)} method. - */ - @Nonnull - protected abstract EntityT getEntity(); - - @SuppressWarnings( "unchecked" ) - @Override - @Nonnull - protected Class getEntityClass() - { - return (Class) getEntity().getClass(); - } - - @Override - @Nonnull - public ModificationResponse executeRequest( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - - final ODataRequestResultGeneric result = toRequest().execute(httpClient); - - return ModificationResponse.of(result, getEntity(), destination); - } - - @Override - @Nonnull - public ODataRequestCreate toRequest() - { - final EntityT entity = getEntity(); - final ODataResourcePath resourcePath; - - if( linkFromParentEntity != null && parentEntity != null ) { - resourcePath = - ODataResourcePath - .of(parentEntity.getEntityCollection(), ODataEntityKey.of(parentEntity.getKey(), ODataProtocol.V2)) - .addSegment(linkFromParentEntity.getFieldName()); - } else { - resourcePath = ODataResourcePath.of(getEntityCollection()); - } - - final String serializedEntity = - Try - .of(() -> ODataEntitySerializer.serializeEntityForCreate(entity)) - .getOrElseThrow( - e -> new ODataSerializationException( - new ODataRequestCreate(getServicePath(), resourcePath, "", ODataProtocol.V2), - entity, - "Failed to serialize HTTP request entity of type " + getEntityClass().getSimpleName(), - e)); - - final ODataRequestCreate request = - new ODataRequestCreate(getServicePath(), resourcePath, serializedEntity, ODataProtocol.V2); - - return super.addHeadersAndCustomParameters(request); - } - - /** - * This function allows to create a new entity via an existing parent entity. Parent means that the existing entity - * has to be related to the entity to be created via a navigation property. Thus, the function requires the caller - * to provide an {@code EntityLink} that represents such a navigation property. - * {@code ParentEntityT} can represent any entity that is related to the entity to be created. {@code EntityT} - * represents the type of the entity to be created. Furthermore, the function requires an instance of type - * {@code ParentEntityT}. This instance must hold the key fields used to identify it. - * - * NOTE: While any EntityLink provided by the OData VDM satisfying these type constraints allows you to call this - * function, the service may NOT allow this kind of create operation for the respective navigation property. Thus, - * calling this function without knowledge of the underlying OData service can result in failing requests. - * - * @param entity - * An instance of the related entity that MUST hold values for the respective key fields. - * @param entityLink - * An {@link EntityLink} representing a navigation property. - * - * @return The same fluent helper configured to use the provided navigation property for creation. - * @param - * The generic parent entity type in this navigation property relation. - */ - @Nonnull - public > FluentHelperT asChildOf( - @Nullable final ParentEntityT entity, - @Nullable final EntityLink, ParentEntityT, EntityT> entityLink ) - { - linkFromParentEntity = entityLink; - parentEntity = entity; - - return getThis(); - } - - @Nonnull - private String getEntityCollection() - { - return Option.of(entityCollection).getOrElse(() -> getEntity().getEntityCollection()); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperDelete.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperDelete.java deleted file mode 100644 index d343548d35..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperDelete.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.request.ETagSubmissionStrategy; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestDelete; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import io.vavr.control.Option; - -/** - * Representation of an OData delete request as a fluent interface for further configuring the request and - * {@link #executeRequest(Destination) executing} it. - * - * @param - * The fluent helper type. - * @param - * The type of the entity to delete. - */ -public abstract class FluentHelperDelete> - extends - FluentHelperModification -{ - private ETagSubmissionStrategy eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_ETAG_FROM_ENTITY; - - /** - * Instantiates this fluent helper using the given service path and entity collection to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityCollection - * The entity collection to direct the requests to. - */ - public FluentHelperDelete( @Nonnull final String servicePath, @Nonnull final String entityCollection ) - { - super(servicePath, entityCollection); - } - - /** - * The entity object to be deleted by calling the {@link #executeRequest(Destination)} method. - * - * @return The entity to be deleted. - */ - @Nonnull - protected abstract EntityT getEntity(); - - @SuppressWarnings( "unchecked" ) - @Override - @Nonnull - protected Class getEntityClass() - { - return (Class) getEntity().getClass(); - } - - /** - * The delete request will ignore any version identifier present on the entity and not send an `If-Match` header. - *

- * Warning: This might lead to a response from the remote system that the `If-Match` header is missing. - *

- * It depends on the implementation of the remote system whether the `If-Match` header is expected. - * - * @return The same request builder that will not send the `If-Match` header in the update request - */ - @Nonnull - public FluentHelperT disableVersionIdentifier() - { - eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_NO_ETAG; - return getThis(); - } - - /** - * The delete request will ignore any version identifier present on the entity and delete the entity, regardless of - * any changes on the remote entity. - *

- * Warning: Be careful with this option, as this might overwrite any changes made to the remote - * representation of this object. - * - * @return The same request builder that will ignore the version identifier of the entity to update - */ - @Nonnull - public FluentHelperT matchAnyVersionIdentifier() - { - eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_ANY_MATCH_ETAG; - return getThis(); - } - - @Override - @Nonnull - public ModificationResponse executeRequest( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - - final ODataRequestResultGeneric result = toRequest().execute(httpClient); - - return ModificationResponse.of(result, getEntity(), destination); - } - - @Override - @Nonnull - public ODataRequestDelete toRequest() - { - final EntityT entity = getEntity(); - final String versionIdentifier = - eTagSubmissionStrategy.getHeaderFromVersionIdentifier(entity.getVersionIdentifier()); - - final ODataRequestDelete request = - new ODataRequestDelete( - getServicePath(), - getEntityCollection(), - ODataEntityKey.of(entity.getKey(), ODataProtocol.V2), - versionIdentifier, - ODataProtocol.V2); - - return super.addHeadersAndCustomParameters(request); - } - - @Nonnull - private String getEntityCollection() - { - return Option.of(entityCollection).getOrElse(() -> getEntity().getEntityCollection()); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperExecutable.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperExecutable.java deleted file mode 100644 index 5d7e2a7864..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperExecutable.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException; -import com.sap.cloud.sdk.cloudplatform.connectivity.exception.HttpClientInstantiationException; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException; - -/** - * Representation of any OData V2 request that can be executed. - * - * @param - * The type of the result entity, if any. - */ -public interface FluentHelperExecutable -{ - /** - * Executes this request. - * - * @param destination - * The target system this request should be issued against. - * @return A response according to the query criteria. - * - * @throws DestinationAccessException - * If there is an issue accessing the - * {@link com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination}. - * @throws HttpClientInstantiationException - * If there is an issue creating the {@link HttpClient}. - * @throws ODataException - * If the OData request execution failed. - */ - @Nullable - ResultT executeRequest( @Nonnull final Destination destination ); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperFactory.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperFactory.java deleted file mode 100644 index e88436afed..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperFactory.java +++ /dev/null @@ -1,280 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.net.URI; -import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.Function; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.classic.methods.HttpPost; -import org.apache.hc.client5.http.classic.methods.HttpUriRequest; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; - -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; - -/** - * Utility class to conveniently create fluent helper instances. - */ -@RequiredArgsConstructor( access = AccessLevel.PRIVATE ) -class FluentHelperFactory -{ - @Nonnull - private final String servicePath; - - static FluentHelperFactory withServicePath( @Nonnull final String servicePath ) - { - return new FluentHelperFactory(servicePath); - } - - < - FluentHelperT extends FluentHelperByKey, EntityT extends VdmEntity, SelectableT> - FluentHelperByKey - readByKey( - @Nonnull final Class entityClass, - @Nonnull final String entityCollection, - @Nonnull final Map key ) - { - return new FluentHelperByKey<>(servicePath, entityCollection) - { - @Nonnull - @Override - protected Map getKey() - { - return key; - } - - @Nonnull - @Override - protected Class getEntityClass() - { - return entityClass; - } - }; - } - - < - FluentHelperT extends FluentHelperRead, EntityT extends VdmEntity, SelectableT> - FluentHelperRead - read( @Nonnull final Class entityClass, @Nonnull final String entityCollection ) - { - return new FluentHelperRead<>(servicePath, entityCollection) - { - @Nonnull - @Override - protected Class getEntityClass() - { - return entityClass; - } - }; - } - - < - FluentHelperT extends FluentHelperCreate, EntityT extends VdmEntity> - FluentHelperCreate - create( @Nonnull final String entityCollection, @Nonnull final EntityT entity ) - { - return new FluentHelperCreate<>(servicePath, entityCollection) - { - @Nonnull - @Override - protected EntityT getEntity() - { - return entity; - } - - @SuppressWarnings( "unchecked" ) - @Nonnull - @Override - protected Class getEntityClass() - { - return (Class) entity.getClass(); - } - }; - } - - < - FluentHelperT extends FluentHelperCreate, EntityT extends VdmEntity> - FluentHelperCreate - create( @Nonnull final EntityT entity ) - { - return create(entity.getEntityCollection(), entity); - } - - < - FluentHelperT extends FluentHelperDelete, EntityT extends VdmEntity> - FluentHelperDelete - delete( @Nonnull final String entityCollection, @Nonnull final EntityT entity ) - { - return new FluentHelperDelete<>(servicePath, entityCollection) - { - @Nonnull - @Override - protected EntityT getEntity() - { - return entity; - } - - @SuppressWarnings( "unchecked" ) - @Nonnull - @Override - protected Class getEntityClass() - { - return (Class) entity.getClass(); - } - }; - } - - < - FluentHelperT extends FluentHelperDelete, EntityT extends VdmEntity> - FluentHelperDelete - delete( @Nonnull final EntityT entity ) - { - return delete(entity.getEntityCollection(), entity); - } - - < - FluentHelperT extends FluentHelperUpdate, EntityT extends VdmEntity> - FluentHelperUpdate - update( @Nonnull final String entityCollection, @Nonnull final EntityT entity ) - { - return new FluentHelperUpdate<>(servicePath, entityCollection) - { - @Nonnull - @Override - protected EntityT getEntity() - { - return entity; - } - - @SuppressWarnings( "unchecked" ) - @Nonnull - @Override - protected Class getEntityClass() - { - return (Class) entity.getClass(); - } - }; - } - - < - FluentHelperT extends FluentHelperUpdate, EntityT extends VdmEntity> - FluentHelperUpdate - update( @Nonnull final EntityT entity ) - { - return update(entity.getEntityCollection(), entity); - } - - < - FluentHelperT extends FluentHelperFunction, ObjectT, ResultT> - FluentHelperFunction - function( - @Nonnull final Map parameters, - @Nonnull final String functionName, - @Nonnull final Class objectClass, - @Nonnull final Function requestHandler, - @Nonnull final BiFunction, Destination, ResultT> executeHandler ) - { - return new FluentHelperFunction<>(servicePath) - { - @Nonnull - @Override - protected Map getParameters() - { - return parameters; - } - - @Nonnull - @Override - protected String getFunctionName() - { - return functionName; - } - - @Nonnull - @Override - protected HttpUriRequest createRequest( @Nonnull final URI uri ) - { - return requestHandler.apply(uri); - } - - @Nonnull - @Override - protected Class getEntityClass() - { - return objectClass; - } - - @Nullable - @Override - public ResultT executeRequest( @Nonnull final Destination destination ) - { - return executeHandler.apply(this, destination); - } - - /** - * {@inheritDoc} The special treatment for SAP S/4 HANA OData v2 responses is considered. - */ - @Override - @Nullable - protected JsonElement refineJsonResponse( @Nullable JsonElement jsonElement ) - { - if( jsonElement instanceof JsonObject && ((JsonObject) jsonElement).has(getFunctionName()) ) { - jsonElement = ((JsonObject) jsonElement).get(getFunctionName()); - } - return super.refineJsonResponse(jsonElement); - } - }; - } - - < - FluentHelperT extends FluentHelperFunction, ObjectT> - FluentHelperFunction - functionSinglePost( - @Nonnull final Map parameters, - @Nonnull final String functionName, - @Nonnull final Class objectClass ) - { - return function(parameters, functionName, objectClass, HttpPost::new, FluentHelperFunction::executeSingle); - } - - < - FluentHelperT extends FluentHelperFunction, ObjectT> - FluentHelperFunction - functionSingleGet( - @Nonnull final Map parameters, - @Nonnull final String functionName, - @Nonnull final Class objectClass ) - { - return function(parameters, functionName, objectClass, HttpGet::new, FluentHelperFunction::executeSingle); - } - - < - FluentHelperT extends FluentHelperFunction>, ObjectT> - FluentHelperFunction> - functionMultiplePost( - @Nonnull final Map parameters, - @Nonnull final String functionName, - @Nonnull final Class objectClass ) - { - return function(parameters, functionName, objectClass, HttpPost::new, FluentHelperFunction::executeMultiple); - } - - < - FluentHelperT extends FluentHelperFunction>, ObjectT> - FluentHelperFunction> - functionMultipleGet( - @Nonnull final Map parameters, - @Nonnull final String functionName, - @Nonnull final Class objectClass ) - { - return function(parameters, functionName, objectClass, HttpGet::new, FluentHelperFunction::executeMultiple); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperFunction.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperFunction.java deleted file mode 100644 index fe7704127b..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperFunction.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.net.URI; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.classic.methods.HttpPost; -import org.apache.hc.client5.http.classic.methods.HttpUriRequest; - -import com.google.gson.JsonElement; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataFunctionParameters; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestAction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestFunction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import io.vavr.Lazy; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of any OData function import as a fluent interface for further configuring the request and - * {@link #executeRequest(Destination) executing} it. - * - * @param - * The fluent helper type. - * @param - * The type of the object this OData request operates on, if any. - * @param - * The type of the result entity, if any. - */ -@Slf4j -public abstract class FluentHelperFunction - extends - FluentHelperBasic -{ - /** - * Upper-case HTTP method derived from {@link #createRequest(URI)} method. - */ - @SuppressWarnings( "this-escape" ) - private final Lazy httpMethod = Lazy.of(() -> createRequest(URI.create("")).getMethod().toUpperCase()); - - /** - * Instantiates this fluent helper using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - */ - public FluentHelperFunction( @Nonnull final String servicePath ) - { - super(servicePath, null); - } - - /** - * Getter for the map of parameters to be used in the function call. - *

- * The map maps the ODataName of a parameter to the corresponding unserialized value. - *

- * Only literal values are allowed as value. No complex or entity objects. - * - * @return A map containing the parameter for the function call. - */ - @Nonnull - protected abstract Map getParameters(); - - /** - * The exact name of the function to be called on the OData Endpoint. - * - * @return The function name on the endpoint. - */ - @Nonnull - protected abstract String getFunctionName(); - - /** - * Creates a request for this function based on the given {@code URI}. - *

- * Examples for such requests are {@code HttpGet} and {@code HttpPost}. - * - * @param uri - * The {@code URI} the request should target. - * @return The instantiated request. - */ - @Nonnull - protected abstract HttpUriRequest createRequest( @Nonnull final URI uri ); - - /** - * {@inheritDoc} - *

- * The function import arguments are encoded as HTTP query expressions. - */ - @Nonnull - @Override - public ODataRequestGeneric toRequest() - { - final ODataRequestGeneric functionImportRequest = instantiateRequest(); - return super.addHeadersAndCustomParameters(functionImportRequest); - } - - @Nonnull - private ODataRequestGeneric instantiateRequest() - { - final String servicePath = getServicePath(); - final ODataResourcePath resourcePath = ODataResourcePath.of(getFunctionName()); - - // get and encode function import parameters as query expressions - final String encodedQuery = ODataFunctionParameters.of(getParameters(), ODataProtocol.V2).toEncodedString(); - - switch( httpMethod.get() ) { - case HttpGet.METHOD_NAME: - return new ODataRequestFunction(servicePath, resourcePath, encodedQuery, ODataProtocol.V2); - case HttpPost.METHOD_NAME: - return new ODataRequestAction(servicePath, resourcePath, null, encodedQuery, ODataProtocol.V2); - default: - throw new IllegalStateException("Unexpected HTTP request method for function import: " + httpMethod); - } - } - - /** - * Default implementation for the case that this function returns a single type. - *

- * This method can be used in subclasses to implement the {@link #executeRequest(Destination)} method. - * - * @param destination - * The destination to run the function against. - * - * @return The single object returned by the function call. Returns {@code null} if the function did not return a - * result. - * - * @throws ODataException - * If the execution of the function failed. - */ - @SuppressWarnings( "checkstyle:IllegalCatch" ) - @Nullable - protected ObjectT executeSingle( @Nonnull final Destination destination ) - throws ODataException - { - final ODataRequestResultGeneric result = executeInternal(destination); - final Class resultType = getEntityClass(); - - if( resultType.equals(Void.class) ) { - return null; - } - - final ObjectT resultObject = result.as(resultType, this::refineJsonResponse); - - if( resultObject instanceof VdmEntity ) { - final VdmEntity entity = (VdmEntity) resultObject; - - // use version identifier from header if present - result.getVersionIdentifierFromHeader().peek(entity::setVersionIdentifier); - - entity.attachToService(getServicePath(), destination); - } - return resultObject; - } - - /** - * Default implementation for the case that this function returns a collection of entries. - *

- * This method can be used in subclasses to implement the {@link #executeRequest(Destination)} method. - * - * @param destination - * The destination to run the function against. - * - * @return A list of the objects returned by the function call. Returns an empty list if the function did not return - * a result. - * - * @throws ODataException - * If the execution of the function failed. - */ - @Nonnull - @SuppressWarnings( "unchecked" ) - protected List executeMultiple( @Nonnull final Destination destination ) - throws ODataException - { - final ODataRequestResultGeneric result = executeInternal(destination); - - final List resultObjectList = (List) result.asList(getEntityClass()); - - if( !resultObjectList.isEmpty() && resultObjectList.get(0) instanceof VdmEntity ) { - resultObjectList.forEach(entity -> ((VdmEntity) entity).attachToService(getServicePath(), destination)); - } - return resultObjectList; - } - - @Nonnull - private ODataRequestResultGeneric executeInternal( final Destination destination ) - throws ODataException - { - final ODataRequestGeneric functionImportRequest = toRequest(); - return (ODataRequestResultGeneric) functionImportRequest.execute(getHttpClient(destination)); - } - - /** - * Transform the JSON element from the response to extract a result entity. By default this method returns the - * original object. - * - * @param jsonElement - * The optional response JSON element - * @return The refined JSON element - */ - @Nullable - protected JsonElement refineJsonResponse( @Nullable final JsonElement jsonElement ) - { - return jsonElement; - } - - /** - * Activates CSRF token retrieval for this OData request. - * - * @return The same fluent helper that will now fetch a CSRF token. - * @deprecated CSRF token handling is now performed automatically by the underlying HTTP client. This method is a - * no-op retained only for source compatibility. It is scheduled for removal. - */ - @Deprecated - @Nonnull - public FluentHelperT withCsrfToken() - { - return getThis(); - } - - /** - * Deactivates the CSRF token retrieval for this OData request. This is useful if the server does not support or - * require CSRF tokens as part of the request. - * - * @return The same builder - */ - @Nonnull - public FluentHelperT withoutCsrfToken() - { - setSkipCsrfTokenRetrieval(); - return getThis(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperModification.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperModification.java deleted file mode 100644 index bf4dac79a1..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperModification.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/** - * Representation of an OData modification request (Create, Update, Delete) as a fluent interface for further - * configuring the request and {@link #executeRequest(Destination) executing} it. - * - * @param - * The fluent helper type. - * @param - * The type of the entity this OData request operates on, if any. - */ -public abstract class FluentHelperModification> - extends - FluentHelperBasic> -{ - /** - * Instantiates this fluent helper using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityCollection - * The entity collection to direct the requests to. - */ - public FluentHelperModification( @Nonnull final String servicePath, @Nullable final String entityCollection ) - { - super(servicePath, entityCollection); - } - - /** - * Deactivates the CSRF token retrieval for this OData request. This is useful if the server does not support or - * require CSRF tokens as part of the request. - * - * @return The same builder - */ - @Nonnull - public FluentHelperT withoutCsrfToken() - { - setSkipCsrfTokenRetrieval(); - return getThis(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperRead.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperRead.java deleted file mode 100644 index 7e3e67b10f..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperRead.java +++ /dev/null @@ -1,297 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Streams; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.query.StructuredQuery; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCount; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; - -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an OData query as a fluent interface for further configuring the request and - * {@link #executeRequest(Destination) executing} it. - * - * @param - * The fluent helper type. - * @param - * The type of the result entity. - * @param - * The type of the {@link EntityField class that represents fields of the entity}. - */ -@Slf4j -public abstract class FluentHelperRead, SelectableT> - extends - FluentHelperBasic> -{ - - private final StructuredQuery delegateQuery; - - /** - * Instantiates this fluent helper using the given service path and entity collection to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * - * @param entityCollection - * The entity collection to direct the requests to. - */ - public FluentHelperRead( @Nonnull final String servicePath, @Nonnull final String entityCollection ) - { - super(servicePath, entityCollection); - - delegateQuery = StructuredQuery.onEntity(entityCollection, ODataProtocol.V2); - } - - @Override - @Nonnull - public ODataRequestRead toRequest() - { - final String queryString = delegateQuery.getEncodedQueryString(); - final ODataRequestRead request = - new ODataRequestRead(getServicePath(), entityCollection, queryString, ODataProtocol.V2); - return super.addHeadersAndCustomParameters(request); - } - - @Override - @Nonnull - public FluentHelperT withQueryParameter( @Nonnull final String key, @Nullable final String value ) - { - return super.withQueryParameter(key, value); - } - - /** - * Query modifier to limit which field values of the entity get fetched and populated, and to specify which - * navigation properties to expand. If this method is never called, then all fields will be fetched and populated, - * and no navigation properties expanded. But if this method is called at least once, then only the specified fields - * will be fetched and populated. Calling this multiple times will combine the set(s) of fields and expansions of - * each call. - * - * @param fields - * Fields to select and/or navigation properties to expand. - * @return The same fluent helper with this query modifier applied. - */ - @SuppressWarnings( "unchecked" ) - @Nonnull - public FluentHelperT select( @Nonnull final SelectableT... fields ) - { - final Iterable> selectableFields = (Iterable>) Arrays.asList(fields); - return super.select(selectableFields, delegateQuery::select, delegateQuery::select); - } - - /** - * Query modifier to sort the set of returned entities by one or more fields. If this method is called more than - * once, then the result entity set will be sorted by each field in the order that the methods were called. - * - * @param field - * Field to sort by. - * @param order - * Sorting direction to use (ascending or descending). - * - * @return The same fluent helper with this query modifier applied. - */ - @Nonnull - public FluentHelperT orderBy( @Nonnull final EntityField field, @Nonnull final Order order ) - { - final com.sap.cloud.sdk.datamodel.odata.client.query.Order clientTypeOrder = - com.sap.cloud.sdk.datamodel.odata.client.query.Order.valueOf(order.toString()); - delegateQuery.orderBy(field.getFieldName(), clientTypeOrder); - - return getThis(); - } - - /** - * Query modifier to limit the number of entities returned. If this method is never called, then the result entity - * list will not be limited in size. If this method is called multiple times, then only the value of the last call - * will be used. - * - * @param top - * Number of entities to limit the result set to. - * @return The same fluent helper with this query modifier applied. - */ - @Nonnull - public FluentHelperT top( @Nonnull final Number top ) - { - delegateQuery.top(top); - return getThis(); - } - - /** - * Query modifier to not return the first N entities of the result set. If this method is never called, then the - * full list will be returned from the first entity. If this method is called multiple times, then only the value of - * the last call will be used. - * - * @param skip - * Number of entities to skip by. - * @return The same fluent helper with this query modifier applied. - */ - @Nonnull - public FluentHelperT skip( @Nonnull final Number skip ) - { - delegateQuery.skip(skip); - return getThis(); - } - - @Override - @Nonnull - public List executeRequest( @Nonnull final Destination destination ) - throws com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException - { - final Iterable> iterablePages = iteratingPages().executeRequest(destination); - final Iterable iterableItems = Iterables.concat(Objects.requireNonNull(iterablePages)); - return Lists.newArrayList(iterableItems); - } - - /** - * Manually explore the individual pages from the lazy-loading entity result-set. The returning object allows for - * performant consumption of all data through server-driven pagination. - * - * @return An instance of {@link FluentHelperExecutable} that allows for lazy iteration through the result-set - * pages. - */ - @Nonnull - public FluentHelperExecutable>> iteratingPages() - { - return this::executeInternal; - } - - /** - * Manually explore the individual entities from the lazy-loading entity result-set. The returning iterable allows - * for performant consumption of all data through server-driven pagination. - * - * @return An instance of {@link FluentHelperExecutable} that allows for lazy iteration through the result-set - * pages. - */ - @Nonnull - public FluentHelperExecutable> iteratingEntities() - { - // concat applies lazy evaluation so individual pages will still be loaded lazily - return destination -> Iterables.concat(executeInternal(destination)); - } - - /** - * Manually explore the individual entities from the lazy-loading entity result-set. The returning Stream allows for - * performant consumption of all data through server-driven pagination. - * - * @return An instance of {@link FluentHelperExecutable} that allows for lazy iteration through the result-set - * pages. - */ - @Nonnull - public FluentHelperExecutable> streamingEntities() - { - // concat applies lazy evaluation so individual pages will still be loaded lazily - return destination -> Streams.stream(Iterables.concat(executeInternal(destination))); - } - - @Nonnull - private Iterable> executeInternal( @Nonnull final Destination destination ) - throws com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException - { - final HttpClient httpClient = getHttpClient(destination); - final Iterable> result = toRequest().execute(httpClient).iteratePages(getEntityClass()); - - // Refine lazy iterable to attach destination properties to individual entities in the page lists. - //noinspection StaticPseudoFunctionalStyleMethod,ConstantConditions - return Iterables - .transform( - result, - list -> list - .stream() - .peek(entity -> entity.attachToService(getServicePath(), destination)) // enable lazy loading for navigation properties on entity - .collect(Collectors.toList())); - } - - /** - * Query modifier to restrict the set of returned entities based on the values of one or more fields. If this method - * is never called, then all accessible entities will be fetched. Calling this multiple times will combine the - * filters into one that is the intersection of all filters (filter1 AND filter2 AND ... filterN). - * - * @param expression - * Fluent helper that represents a field value expression. To create this, start with an - * {@link com.sap.cloud.sdk.datamodel.odata.helper.EntityField EntityField} constant in your respective - * entity type for the desired entity field. Then call one of the comparison operators and provide a - * comparison value. Optionally the resulting fluent helper can be chained with other expression fluent - * helpers. - * - * @return The same fluent helper with this query modifier applied. - */ - @Nonnull - public FluentHelperT filter( @Nonnull final ExpressionFluentHelper expression ) - { - delegateQuery.filter(expression.getDelegateExpressionWithoutOuterParentheses()); - return getThis(); - } - - /** - * Query modifier to return only the number of tuples that match the criteria specified in the query. The actual - * tuples are not returned. May be used with all other query modifiers. Any call to top is overloaded with top(0). - * Any call to skip is ignored. - * - * @return The number of tuples that match the criteria specified in the query. - */ - @Nonnull - public FluentHelperCount count() - { - final StructuredQuery prunedQuery = StructuredQuery.onEntity(entityCollection, ODataProtocol.V2); - - delegateQuery.getFilters().forEach(prunedQuery::filter); - delegateQuery.getCustomParameters().forEach(prunedQuery::withCustomParameter); - - final ODataRequestCount requestCount = - new ODataRequestCount( - getServicePath(), - entityCollection, - prunedQuery.getEncodedQueryString(), - ODataProtocol.V2); - - final ODataRequestCount requestCountUpdated = addHeadersAndCustomParameters(requestCount); - - return new FluentHelperCount(requestCountUpdated); - } - - /** - * Set the preferred page size of the OData response. A result-set may be split into multiple pages, each including - * a subset of the entities matching the query. - *

- * Note: The OData service might ignore the preferred page size setting and may not use pagination - * at all. - * - * @param size - * The preferred page size - * @return This request object with the added parameter. - */ - @Nonnull - public FluentHelperT withPreferredPageSize( final int size ) - { - return withHeader("Prefer", "odata.maxpagesize=" + size); - } - - /** - * Activates CSRF token retrieval for this OData request. - * - * @return The same fluent helper that will now fetch a CSRF token. - * @deprecated CSRF token handling is now performed automatically by the underlying HTTP client. This method is a - * no-op retained only for source compatibility, as read requests never require a CSRF token. It is - * scheduled for removal. - */ - @Deprecated - @Nonnull - public FluentHelperT withCsrfToken() - { - return getThis(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperUpdate.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperUpdate.java deleted file mode 100644 index f676b33ec9..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperUpdate.java +++ /dev/null @@ -1,292 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.stream.Collectors; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.google.common.annotations.Beta; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataSerializationException; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; -import com.sap.cloud.sdk.datamodel.odata.client.request.ETagSubmissionStrategy; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestUpdate; -import com.sap.cloud.sdk.datamodel.odata.client.request.UpdateStrategy; - -import io.vavr.control.Option; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an OData update request as a fluent interface for further configuring the request and - * {@link #executeRequest(Destination) executing} it. - * - * @param - * The fluent helper type. - * @param - * The type of the entity to update. - */ -@Slf4j -public abstract class FluentHelperUpdate> - extends - FluentHelperModification -{ - private final Collection> includedFields = new HashSet<>(); - private final Collection> excludedFields = new HashSet<>(); - private UpdateStrategy updateStrategy = UpdateStrategy.MODIFY_WITH_PATCH; - private ETagSubmissionStrategy eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_ETAG_FROM_ENTITY; - - /** - * Instantiates this fluent helper using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityCollection - * The entity collection to direct the requests to. - */ - public FluentHelperUpdate( @Nonnull final String servicePath, @Nonnull final String entityCollection ) - { - super(servicePath, entityCollection); - } - - /** - * The entity object to be updated by calling the {@link #executeRequest(Destination)} method. - * - * @return The entity to be updated. - */ - protected abstract EntityT getEntity(); - - @SuppressWarnings( "unchecked" ) - @Nonnull - @Override - protected Class getEntityClass() - { - return (Class) getEntity().getClass(); - } - - /** - * The update request will ignore any version identifier present on the entity and not send an `If-Match` header. - *

- * Warning: This might lead to a response from the remote system that the `If-Match` header is missing. - *

- * It depends on the implementation of the remote system whether the `If-Match` header is expected. - * - * @return The same request builder that will not send the `If-Match` header in the update request - */ - @Nonnull - public FluentHelperT disableVersionIdentifier() - { - eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_NO_ETAG; - return getThis(); - } - - /** - * The update request will ignore any version identifier present on the entity and update the entity, regardless of - * any changes on the remote entity. - *

- * Warning: Be careful with this option, as this might overwrite any changes made to the remote - * representation of this object. - * - * @return The same request builder that will ignore the version identifier of the entity to update - */ - @Nonnull - public FluentHelperT matchAnyVersionIdentifier() - { - eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_ANY_MATCH_ETAG; - return getThis(); - } - - @Override - @Nonnull - public ModificationResponse executeRequest( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - - final ODataRequestResultGeneric result = toRequest().execute(httpClient); - - return ModificationResponse.of(result, getEntity(), destination); - } - - @Override - @Nonnull - public ODataRequestUpdate toRequest() - { - final EntityT entity = getEntity(); - final String serializedEntity = getSerializedEntity(); - final String versionIdentifier = - eTagSubmissionStrategy.getHeaderFromVersionIdentifier(entity.getVersionIdentifier()); - - final ODataRequestUpdate request = - new ODataRequestUpdate( - getServicePath(), - getEntityCollection(), - ODataEntityKey.of(entity.getKey(), ODataProtocol.V2), - serializedEntity, - updateStrategy, - versionIdentifier, - ODataProtocol.V2); - - return super.addHeadersAndCustomParameters(request); - } - - @Nonnull - private String getSerializedEntity() - { - final EntityT entity = getEntity(); - try { - final List fieldsToExcludeUpdate = - excludedFields - .stream() - .map(EntitySelectable::getFieldName) - .map(FieldReference::of) - .collect(Collectors.toList()); - - final List fieldsToIncludeInUpdate = - includedFields - .stream() - .map(EntitySelectable::getFieldName) - .map(FieldReference::of) - .collect(Collectors.toList()); - - switch( updateStrategy ) { - case REPLACE_WITH_PUT: - return ODataEntitySerializer.serializeEntityForUpdatePut(entity, fieldsToExcludeUpdate); - case MODIFY_WITH_PATCH: - return ODataEntitySerializer.serializeEntityForUpdatePatchShallow(entity, fieldsToIncludeInUpdate); - case MODIFY_WITH_PATCH_RECURSIVE_DELTA: - return ODataEntitySerializer - .serializeEntityForUpdatePatchRecursiveDelta(entity, fieldsToIncludeInUpdate); - case MODIFY_WITH_PATCH_RECURSIVE_FULL: - return ODataEntitySerializer - .serializeEntityForUpdatePatchRecursiveFull(entity, fieldsToIncludeInUpdate); - default: - throw new IllegalStateException("Unexpected update strategy:" + updateStrategy); - } - } - catch( final Exception e ) { - final String msg = - String - .format( - "Failed to serialize OData Update HTTP request entity for type %s with strategy %s", - getEntityClass().getSimpleName(), - updateStrategy); - - final ODataRequestUpdate request = - new ODataRequestUpdate( - getServicePath(), - entity.getEntityCollection(), - ODataEntityKey.of(entity.getKey(), ODataProtocol.V2), - "", - updateStrategy, - eTagSubmissionStrategy.getHeaderFromVersionIdentifier(entity.getVersionIdentifier()), - ODataProtocol.V2); - throw new ODataSerializationException(request, entity, msg, e); - } - } - - /** - * Allows to explicitly specify entity fields that shall be sent in an update request regardless if the values of - * these fields have been changed. This is helpful in case the API requires to send certain fields in any case in an - * update request. - * - * @param fields - * The fields to be included in the update execution. - * @return The same fluent helper which will include the specified fields in an update request. - */ - @Nonnull - @SafeVarargs - @SuppressWarnings( "varargs" ) - public final FluentHelperT includingFields( @Nonnull final EntitySelectable... fields ) - { - includedFields.addAll(Arrays.asList(fields)); - return getThis(); - } - - /** - * Allows to explicitly specify entity fields that should not be sent in an update request. This is helpful in case, - * some services require no read only fields to be sent for update requests. These fields are only excluded in a PUT - * request, they are not considered in a PATCH request. - * - * @param fields - * The fields to be excluded in the update execution. - * @return The same fluent helper which will exclude the specified fields in an update request. - */ - @Nonnull - @SafeVarargs - @SuppressWarnings( "varargs" ) - public final FluentHelperT excludingFields( @Nonnull final EntitySelectable... fields ) - { - excludedFields.addAll(Arrays.asList(fields)); - return getThis(); - } - - @Nonnull - private String getEntityCollection() - { - return Option.of(entityCollection).getOrElse(() -> getEntity().getEntityCollection()); - } - - /** - * Allows to control that the request to update the entity is sent with the HTTP method PUT and its payload contains - * all fields of the entity, regardless which of them have been changed. - * - * @return The same fluent helper which will replace the entity in the remote system - */ - @Nonnull - public final FluentHelperT replacingEntity() - { - updateStrategy = UpdateStrategy.REPLACE_WITH_PUT; - return getThis(); - } - - /** - * Allows to control that the request to update the entity is sent with the HTTP method PATCH and its payload - * contains the changed fields only. - * - * @return The same fluent helper which will modify the entity in the remote system. - */ - @Nonnull - public final FluentHelperT modifyingEntity() - { - updateStrategy = UpdateStrategy.MODIFY_WITH_PATCH; - return getThis(); - } - - /** - * Allows to control that the request to update the entity is sent with the HTTP method PATCH and its payload - * contains the changed fields only, with different strategies for handling nested fields. - * - * @param strategy - * The strategy to use for the PATCH update. - * @return The same fluent helper which will modify the entity in the remote system. - * @throws IllegalArgumentException - * If an unknown ModifyPatchStrategy is provided. - * @since 5.16.0 - */ - @Beta - @Nonnull - public final FluentHelperT modifyingEntity( @Nonnull final ModifyPatchStrategy strategy ) - { - switch( strategy ) { - case SHALLOW: - return modifyingEntity(); - case RECURSIVE_DELTA: - updateStrategy = UpdateStrategy.MODIFY_WITH_PATCH_RECURSIVE_DELTA; - break; - case RECURSIVE_FULL: - updateStrategy = UpdateStrategy.MODIFY_WITH_PATCH_RECURSIVE_FULL; - break; - default: - throw new IllegalArgumentException("Unknown ModifyPatchStrategy: " + strategy); - } - return getThis(); - } - -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ModificationResponse.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ModificationResponse.java deleted file mode 100644 index 227c5baa8d..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ModificationResponse.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.common.annotations.Beta; -import com.google.gson.Gson; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import io.vavr.control.Option; -import io.vavr.control.Try; -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -/** - * Generic OData service response wrapper for data modification requests. - * - * @param - * The generic entity type. - */ -@EqualsAndHashCode -@ToString -@RequiredArgsConstructor( staticName = "of", onConstructor = @__( { @Beta } ), access = AccessLevel.PUBLIC ) -@Slf4j -public final class ModificationResponse> -{ - private static final Gson GSON = new Gson(); - - @Nullable - private EntityT modifiedEntity; - - @Nonnull - private Option responseEntity = Option.none(); - - @Nonnull - private final ODataRequestResultGeneric result; - - @Nonnull - private final EntityT originalRequestEntity; - - @Nonnull - private final Destination destination; - - /** - * Get an updated version of the entity. If the service responded with an entity it is returned here. If the service - * didn't respond with an entity but send an @{code ETag} header, the entity is updated and returned. Otherwise a - * copy of the original, unmodified entity is returned. - * - * @return The modified entity. - */ - @Nonnull - public synchronized EntityT getModifiedEntity() - { - if( modifiedEntity == null ) { - evaluateResponse(); - } - return modifiedEntity; - } - - /** - * Get the optional response entity parsed from the HTTP content. - * - * @return The parsed entity or none. - */ - @Nonnull - public synchronized Option getResponseEntity() - { - // We synchronize the full method here because Fortify does not like double checked locking: - // https://vulncat.fortify.com/en/detail?id=desc.structural.java.code_correctness_double_checked_locking - if( modifiedEntity == null ) { - evaluateResponse(); - } - return responseEntity; - } - - @SuppressWarnings( "unchecked" ) - private void evaluateResponse() - { - responseEntity = parseEntityFromResponse(); - - // create a copy before modifying the version identifier to not change existing objects - final EntityT entityToModify = responseEntity.getOrElse(originalRequestEntity); - - // clone entity - modifiedEntity = GSON.fromJson(GSON.toJson(entityToModify), (Class) originalRequestEntity.getClass()); - - modifiedEntity.attachToService(result.getODataRequest().getServicePath(), destination); - - modifiedEntity.setVersionIdentifier(getUpdatedVersionIdentifier().getOrNull()); - } - - /** - * Access the original entity used to make the request. - * - * @return The original entity object used to perform an OData request. - */ - @Nonnull - public EntityT getRequestEntity() - { - return originalRequestEntity; - } - - /** - * Get the response status code. - * - * @return The integer representation of the HTTP status code. - */ - public int getResponseStatusCode() - { - return result.getHttpResponse().getCode(); - } - - /** - * Get the response headers. - * - * @return The headers of the HTTP status code. - */ - @Nonnull - public Map> getResponseHeaders() - { - return result.getAllHeaderValues(); - } - - /** - * Get the version identifier present in the response headers, the identifier of the original entity or none, if - * neither exist. - * - * @return An up to date version identifier or none. - */ - @Nonnull - public Option getUpdatedVersionIdentifier() - { - return result - .getVersionIdentifierFromHeader() - .orElse(() -> getResponseEntity().flatMap(entity -> entity.getVersionIdentifier())); - } - - @Nonnull - @SuppressWarnings( "unchecked" ) - private Option parseEntityFromResponse() - { - return Try - .of(() -> result.as((Class) originalRequestEntity.getClass())) - .onFailure(e -> log.debug("Failed to parse entity from HTTP response.", e)) - .toOption() - .peek(entity -> result.getVersionIdentifierFromHeader().peek(entity::setVersionIdentifier)); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ModifyPatchStrategy.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ModifyPatchStrategy.java deleted file mode 100644 index e934c50324..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ModifyPatchStrategy.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import com.google.common.annotations.Beta; - -/** - * Strategy to determine how a patch operation should be applied to an entity. - * - * @since 5.16.0 - */ -@Beta -public enum ModifyPatchStrategy -{ - /** Only the top level fields can be patched */ - SHALLOW, - - /** All top level and nested fields can be patched, resulting in JSON containing only the changed fields */ - RECURSIVE_DELTA, - - /** - * All top level and nested fields can be patched, resulting in JSON containing the full value of complex object. - */ - RECURSIVE_FULL -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataEntitySerializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataEntitySerializer.java deleted file mode 100644 index 3898351d0d..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataEntitySerializer.java +++ /dev/null @@ -1,321 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BiPredicate; -import java.util.function.Predicate; -import java.util.stream.StreamSupport; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.sap.cloud.sdk.datamodel.odata.adapter.ODataNumberSerializer; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -final class ODataEntitySerializer -{ - private static final Gson GSON; - private static final Gson GSON_SERIALIZING_NULLS; - - static { - final ODataNumberSerializer numberSerializer = new ODataNumberSerializer(); - final GsonBuilder builder = - new GsonBuilder() - .registerTypeAdapter(Long.class, numberSerializer) - .registerTypeAdapter(Double.class, numberSerializer) - .registerTypeAdapter(Float.class, numberSerializer) - .registerTypeAdapter(BigDecimal.class, numberSerializer); - // CXSDK currently serializes Edm.SByte as plain number so the client also does it for consistency. - // .registerTypeAdapter(Byte.class, numberSerializer) - - GSON = builder.create(); - GSON_SERIALIZING_NULLS = builder.serializeNulls().create(); - } - - /** - * Serializes an entity for update request (PUT). Allowing null values. Removing potential "versionIdentifier" - * fields. - * - * @param entity - * The OData V2 entity reference. - * @param excludedFields - * Collection of fields to be excluded in the update (PUT) request. - * @return The serialized JSON string for entity update request. - */ - @Nonnull - static String serializeEntityForUpdatePut( - @Nonnull final VdmEntity entity, - @Nullable final Collection excludedFields ) - { - final JsonObject jsonObject = GSON_SERIALIZING_NULLS.toJsonTree(entity).getAsJsonObject(); - - removeVersionIdentifier(jsonObject); - - // find field names to be removed from PUT request - if( excludedFields != null ) { - excludedFields.stream().map(FieldReference::getFieldName).forEach(jsonObject::remove); - } - - return GSON_SERIALIZING_NULLS.toJson(jsonObject); - } - - /** - * Serializes an entity for create request. Ignoring empty collections and null values. - * - * @param entity - * The OData V2 entity reference. - * @return The serialized JSON string for entity create request. - */ - @Nonnull - static String serializeEntityForCreate( @Nonnull final VdmEntity entity ) - { - // When using builder pattern, all 1:n navigation properties will be initialized with `new ArrayList()` instead of expected `null` - final JsonObject jsonObject = GSON.toJsonTree(entity).getAsJsonObject(); - - removeEmptyArrays(jsonObject); - - return GSON.toJson(jsonObject); - } - - /** - * Serializes an entity for update request (PATCH). Allowing null values. - * - * @param entity - * The OData V2 entity reference. - * @param includedFields - * Collection of fields to be included in the update (PATCH) request. - * @return The serialized JSON string for entity update request. - */ - @Nonnull - static String serializeEntityForUpdatePatchShallow( - @Nonnull final VdmEntity entity, - @Nonnull final Collection includedFields ) - { - final JsonObject fullEntity = GSON_SERIALIZING_NULLS.toJsonTree(entity).getAsJsonObject(); - - // find field names to be patched - final Set fieldNamesToPatch = new HashSet<>(entity.getChangedFields().keySet()); - includedFields.stream().map(FieldReference::getFieldName).forEach(fieldNamesToPatch::add); - log.debug("The following fields are marked for updates: {}.", fieldNamesToPatch); - - final JsonObject partialEntity = new JsonObject(); - - fieldNamesToPatch.forEach(key -> partialEntity.add(key, fullEntity.get(key))); - - return GSON_SERIALIZING_NULLS.toJson(partialEntity); - } - - /** - * Serializes an entity for update request (PATCH) including changes in nested properties. Allowing null values. - * Resulting JSON contains the full value of complex fields for changing any nested field. - * - * @param entity - * The OData V2 entity reference. - * @param includedFields - * Collection of fields to be included in the update (PATCH) request. - * @return The serialized JSON string for entity update request. - */ - @Nonnull - static String serializeEntityForUpdatePatchRecursiveFull( - @Nonnull final VdmEntity entity, - @Nonnull final Collection includedFields ) - { - final JsonObject fullEntityJson = GSON_SERIALIZING_NULLS.toJsonTree(entity).getAsJsonObject(); - final JsonObject patchObject = new JsonObject(); - - final Set changedFieldNames = new HashSet<>(entity.getChangedFields().keySet()); - includedFields.stream().map(FieldReference::getFieldName).forEach(changedFieldNames::add); - changedFieldNames.forEach(key -> patchObject.add(key, fullEntityJson.get(key))); - - entity - .toMapOfFields() - .entrySet() - .stream() - .filter(entry -> !patchObject.has(entry.getKey())) - .filter(entry -> containsNestedChangedFields(entry.getValue())) - .forEach(entry -> patchObject.add(entry.getKey(), fullEntityJson.get(entry.getKey()))); - - log.debug("The following object is serialized for update : {}.", patchObject); - - return GSON_SERIALIZING_NULLS.toJson(patchObject); - } - - /** - * Checks if the given complex object contains any changed fields in its nested fields. - * - * @param obj - * the complex object to check - * @return true if the complex object contains any changed fields, false otherwise - */ - private static boolean containsNestedChangedFields( final Object obj ) - { - if( obj instanceof VdmComplex vdmComplex ) { - if( !vdmComplex.getChangedFields().isEmpty() ) { - return true; - } - for( final Object complexField : vdmComplex.toMapOfFields().values() ) { - if( containsNestedChangedFields(complexField) ) { - return true; - } - } - } - return false; - } - - /** - * Serializes an entity for update request (PATCH) including changes in nested properties. Allowing null values. - * Resulting JSON contains only the changed fields (including nested changes). - * - * @param entity - * The OData V2 entity reference. - * @param includedFields - * Collection of fields to be included in the update (PATCH) request. - * @return The serialized JSON string for entity update request. - */ - @Nonnull - static String serializeEntityForUpdatePatchRecursiveDelta( - @Nonnull final VdmEntity entity, - @Nonnull final Collection includedFields ) - { - final JsonObject fullEntityJson = GSON_SERIALIZING_NULLS.toJsonTree(entity).getAsJsonObject(); - final JsonObject patchObject = new JsonObject(); - - // Recursively build patch object from changed fields - final JsonObject tempPatchObject = createPatchObjectRecursiveDelta(entity, fullEntityJson); - - // Add included fields (from the root only) - includedFields - .stream() - .map(FieldReference::getFieldName) - .forEach(key -> patchObject.add(key, fullEntityJson.get(key))); - - // Merge all fields from the tempPatchObject if not already present - tempPatchObject - .entrySet() - .stream() - .filter(entry -> !patchObject.has(entry.getKey())) - .forEach(entry -> patchObject.add(entry.getKey(), entry.getValue())); - - log.debug("The following delta object is serialized for update : {}.", patchObject); - - return GSON_SERIALIZING_NULLS.toJson(patchObject); - } - - /** - * Recursively builds a patch object for a VdmObject by including only changed fields. Complex fields are traversed - * recursively. - * - * @param vdmObject - * the VdmObject (entity or complex) to build the patch from - * @param jsonObject - * the full JSON representation of this object - * @return a JsonObject that contains only changed fields (including nested changes) - */ - @Nonnull - private static - JsonObject - createPatchObjectRecursiveDelta( @Nonnull final VdmObject vdmObject, @Nonnull final JsonObject jsonObject ) - { - final JsonObject patch = new JsonObject(); - - // Process all complex fields and recursively build patch for the complex field - vdmObject.toMapOfFields().forEach(( fieldName, val ) -> { - if( val instanceof VdmComplex complexField ) { - final var childJsonObject = jsonObject.getAsJsonObject(fieldName); - final var childJsonObjectDelta = createPatchObjectRecursiveDelta(complexField, childJsonObject); - if( !childJsonObjectDelta.isEmpty() ) { - patch.add(fieldName, childJsonObjectDelta); - } - } - }); - - // Add explicitly changed fields - vdmObject.getChangedFields().keySet().forEach(key -> patch.add(key, jsonObject.get(key))); - - return patch; - } - - private static void removeVersionIdentifier( @Nonnull final JsonObject jsonObject ) - { - log.debug("Removing redundant \"versionIdentifier\" recursively from JSON object: {}", jsonObject); - - final Predicate isNullOrString = - obj -> obj.isJsonNull() || obj.isJsonPrimitive() && obj.getAsJsonPrimitive().isString(); - - traverseJsonObject( - jsonObject, - ( element, name ) -> "versionIdentifier".equals(name) && isNullOrString.test(element), - JsonObject::remove); - - log.debug("JSON object after removing redundant \"versionIdentifier\": {}", jsonObject); - } - - private static void removeEmptyArrays( @Nonnull final JsonObject jsonObject ) - { - log.debug("Removing empty arrays recursively from JSON object: {}", jsonObject); - - traverseJsonObject( - jsonObject, - ( element, name ) -> element.isJsonArray() && element.getAsJsonArray().size() == 0, - JsonObject::remove); - - log.debug("JSON object after removing empty arrays: {}", jsonObject); - } - - /** - * Traverse the JSON object tree to apply an action on filtered items. - * - * @param jsonObject - * The current JSON object to check properties for. - * @param filter - * The filter as lambda (predicate) for property value and property key. - * @param action - * The action as lambda (consumer) for parent object and filtered property key. - */ - private static void traverseJsonObject( - @Nonnull final JsonObject jsonObject, - @Nonnull final BiPredicate filter, - @Nonnull final BiConsumer action ) - { - final List filteredChildKeys = new ArrayList<>(); - - jsonObject.entrySet().forEach(entry -> { - if( entry.getValue().isJsonObject() ) { - // Apply this logic recursively for all nested objects - traverseJsonObject(entry.getValue().getAsJsonObject(), filter, action); - } else if( filter.test(entry.getValue(), entry.getKey()) ) { - // Collect key names for children that tested against the provided filter predicate - filteredChildKeys.add(entry.getKey()); - } else if( entry.getValue().isJsonArray() ) { - // Apply this logic recursively for lists of nested objects - final JsonArray jsonArray = entry.getValue().getAsJsonArray(); - StreamSupport - .stream(jsonArray.spliterator(), false) - .filter(JsonElement::isJsonObject) - .map(JsonElement::getAsJsonObject) - .forEach(o -> traverseJsonObject(o, filter, action)); - } - }); - - log - .trace( - "Applying the provided action on parent element {} for the following child items: {}", - jsonObject, - filteredChildKeys); - - filteredChildKeys.forEach(affectedChildKey -> action.accept(jsonObject, affectedChildKey)); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/OneToOneLink.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/OneToOneLink.java deleted file mode 100644 index bd5f9b329a..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/OneToOneLink.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; - -/** - * Interface designed to be implemented by {@link EntityLink} class in order to provide a filter function. - * - * @param - * The entity where the link starts. - * @param - * The entity where the link ends (i.e. the related entity). - */ -public interface OneToOneLink -{ - /** - * Add filter to a one-to-one navigation property relationship. - * - * @param filterExpression - * The filter expression to use for resolving the navigation property - * @return The fluent helper to continue constructing a filter expression. - */ - @Nonnull - ExpressionFluentHelper filter( @Nonnull final ExpressionFluentHelper filterExpression ); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/Order.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/Order.java deleted file mode 100644 index 7ecf43a135..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/Order.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -/** - * Used with orderBy methods in entity fluent helper objects to set the sorting order of field values. - */ -public enum Order -{ - /** - * Sort field values in ascending order. - */ - ASC, - - /** - * Sort field values in descending order. - */ - DESC -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/SingleValuedFluentHelperFunction.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/SingleValuedFluentHelperFunction.java deleted file mode 100644 index efa3f3c914..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/SingleValuedFluentHelperFunction.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import javax.annotation.Nonnull; - -/** - * Representation of any OData function import as a fluent class for further configuring the request and - * {@link #executeRequest(Destination) executing} it. This is specifically for functions that return either a single - * primitive value or entity value - * - * @param - * The fluent helper type. - * @param - * The type of the object this OData request operates on, if any. - * @param - * The type of the result entity, if any. - */ -public abstract class SingleValuedFluentHelperFunction - extends - FluentHelperFunction -{ - - /** - * Instantiates this fluent helper using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - */ - public SingleValuedFluentHelperFunction( @Nonnull final String servicePath ) - { - super(servicePath); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmComplex.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmComplex.java deleted file mode 100644 index 74297e4257..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmComplex.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import lombok.EqualsAndHashCode; - -/** - * Complex type in the virtual data model. - * - * @param - * Object type of the complex type. - */ -@EqualsAndHashCode( callSuper = true, doNotUseGetters = true ) -public abstract class VdmComplex extends VdmObject -{ - -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntity.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntity.java deleted file mode 100644 index 7da047d602..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntity.java +++ /dev/null @@ -1,226 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.List; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.Iterables; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataRequestException; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import io.vavr.control.Option; -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; - -/** - * Represents a {@link VdmObject} which is an entity. Entities may have a version identifier. - * - * @param - * The specific entity data type. - */ -@EqualsAndHashCode( callSuper = true, doNotUseGetters = true ) -public abstract class VdmEntity extends VdmObject -{ - @SerializedName( "versionIdentifier" ) - @JsonProperty( "versionIdentifier" ) - @Nullable - private String versionIdentifier = null; - - /** - * The service path only used for the fetch commands of this entity. - *

- * Note: Use with caution, as this can easily break the fetch call on this entity. See the interface of the - * corresponding service for the default service path. - */ - @Getter( AccessLevel.PROTECTED ) - @Setter( AccessLevel.PROTECTED ) - @JsonIgnore - @EqualsAndHashCode.Exclude - private transient String servicePathForFetch; - - /** - * Convenience field for reusing the same destination with multiple queries (e.g. fetching associated entities). - */ - @Getter( AccessLevel.PROTECTED ) - @Setter( AccessLevel.PROTECTED ) - @JsonIgnore - @EqualsAndHashCode.Exclude - private transient Destination destinationForFetch; - - /** - * Getter for the version identifier of this entity. - *

- * This identifier can be used to compare this entity with a remote one. As not the whole entity has to be sent this - * reduces the request overhead. - *

- * Actual use cases can be checking whether this entity is still current with regards to the remote entity, and - * ensuring that a update/delete operation is done on the expected version of the remote entity. - * - * @return The optional version identifier. - */ - @Nonnull - public Option getVersionIdentifier() - { - return Option.of(versionIdentifier); - } - - /** - * Setter for the version identifier of this entity. - *

- * This identifier can be used to compare this entity with a remote one. As not the whole entity has to be sent this - * reduces the request overhead. - *

- * Actual use cases can be checking whether this entity is still current with regards to the remote entity, and - * ensuring that a update/delete operation is done on the expected version of the remote entity. - * - * @param versionIdentifier - * The version identifier of this entity. - */ - public void setVersionIdentifier( @Nullable final String versionIdentifier ) - { - this.versionIdentifier = versionIdentifier; - } - - /** - * Used by fluent helpers and navigation property methods to construct OData queries. - * - * @return EDMX name of the entity collection identifier. - */ - @Nonnull - protected abstract String getEntityCollection(); - - /** - * Used by fluent helpers and navigation property methods to construct OData queries. - * - * @return Default context path to the OData service. In other words, everything in between the - * {@code protocol://hostname:port} and the OData resource name (entity set, {@code $metadata}, etc.) - */ - @Nullable - protected String getDefaultServicePath() - { - return null; - } - - /** - * Sets the service path and destination for the fetch commands of this entity. - *

- * Also applies to any associated entities (navigation properties) that were previously fetched. - *

- * Note: Use with caution, as this can easily break the fetch calls on this entity. See the interface of the - * corresponding service for the default service path. - * - * @param servicePath - * Optional parameter. New service path to apply to this entity and any associated entities that were - * previously fetched. If a null value is provided and the service path has never been set, then the - * service path will be set to the default defined in the corresponding service interface. - * @param destination - * New destination to apply to this entity and any associated entities that were previously fetched. - */ - protected void attachToService( @Nullable final String servicePath, @Nonnull final Destination destination ) - { - if( servicePath != null ) { - servicePathForFetch = servicePath; - } else if( servicePathForFetch == null ) { - servicePathForFetch = getDefaultServicePath(); - } - - destinationForFetch = destination; - - toMapOfNavigationProperties().values().forEach(navProperty -> { - if( navProperty instanceof Iterable ) { - final Iterable navPropertyList = (Iterable) navProperty; - final boolean itemTypeIsEntity = Iterables.getFirst(navPropertyList, null) instanceof VdmEntity; - - if( itemTypeIsEntity ) { - for( final Object childEntity : navPropertyList ) { - final VdmEntity vdmEntity = (VdmEntity) childEntity; - vdmEntity.attachToService(servicePathForFetch, destinationForFetch); - } - } - } else if( navProperty instanceof VdmEntity ) { - final VdmEntity vdmEntity = (VdmEntity) navProperty; - vdmEntity.attachToService(servicePathForFetch, destinationForFetch); - } - }); - } - - /** - * Helper method to lazily resolve a field value from current entity. - * - * @param fieldName - * The field name to lookup. - * @param fieldType - * The field type to cast the value to. - * @param - * The generic type parameter. - * @return A list of requested values. - */ - @Nonnull - protected > List fetchFieldAsList( - @Nonnull final String fieldName, - @Nonnull final Class fieldType ) - { - final Destination destination = getDestinationForFetch(); - final ODataRequestResultGeneric response = fetchField(fieldName, destination); - final List entityList = response.asList(fieldType); - for( final T entity : entityList ) { - entity.attachToService(getServicePathForFetch(), destination); - } - return entityList; - } - - /** - * Helper method to lazily resolve a field value from current entity. - * - * @param fieldName - * The field name to lookup. - * @param fieldType - * The field type to cast the value to. - * @param - * The generic type parameter. - * @return The requested values. - */ - @Nonnull - protected > T fetchFieldAsSingle( - @Nonnull final String fieldName, - @Nonnull final Class fieldType ) - { - final Destination destination = getDestinationForFetch(); - final ODataRequestResultGeneric response = fetchField(fieldName, destination); - final T entity = response.as(fieldType); - entity.attachToService(getServicePathForFetch(), destination); - return entity; - } - - @Nonnull - private ODataRequestResultGeneric fetchField( final String fieldName, final Destination destination ) - { - final ODataEntityKey entityKey = ODataEntityKey.of(getKey(), ODataProtocol.V2); - final ODataResourcePath path = ODataResourcePath.of(getEntityCollection(), entityKey).addSegment(fieldName); - final ODataRequestRead request = new ODataRequestRead(getServicePathForFetch(), path, null, ODataProtocol.V2); - if( destination == null ) { - throw new ODataRequestException( - request, - "Failed to fetch related objects from field name " - + fieldName - + ": The entity was created locally without an assigned HttpDestination. This method is applicable only on entities which were retrieved or created using the OData VDM.", - null); - } - final HttpClient httpClient = ODataApacheHttpClient5Accessor.getHttpClient(destination); - return request.execute(httpClient); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntityUtil.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntityUtil.java deleted file mode 100644 index 08d6c65692..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntityUtil.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.lang.reflect.InvocationTargetException; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.cloudplatform.exception.ShouldNotHappenException; - -import lombok.RequiredArgsConstructor; - -/** - * Utility class to manage OData entity deserialization. - * - * @param - * Entity type to create new instances from. - */ -@RequiredArgsConstructor -public final class VdmEntityUtil> -{ - private final Class entityClass; - - @Nonnull - EntityT newInstance() - { - try { - return entityClass.getDeclaredConstructor().newInstance(); - } - catch( final - InstantiationException - | IllegalAccessException - | NoSuchMethodException - | InvocationTargetException e ) { - throw new ShouldNotHappenException( - "Failed to instantiate object of type " + entityClass.getSimpleName(), - e); - } - } - - /** - * Helper method to resolve the expected entity type for the provided fluent helper instance. For internal use. - * - * @param fluentHelper - * The fluent helper instance to resolve the entity type for. - * @param - * The generic entity type. - * @return The entity type. - */ - @SuppressWarnings( "unchecked" ) - @Nonnull - public static Class getEntityClass( @Nonnull final FluentHelperBasic fluentHelper ) - { - return (Class) fluentHelper.getEntityClass(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmMediaEntity.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmMediaEntity.java deleted file mode 100644 index b4977c17be..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmMediaEntity.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.io.IOException; -import java.io.InputStream; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.core5.http.HttpEntity; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataRequestException; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataResponseException; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestReadByKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import lombok.EqualsAndHashCode; - -/** - * Represents a media entity which exposes additional data under a {@code $value} endpoint. - * - * @param - * The specific entity type. - */ -@EqualsAndHashCode( callSuper = true, doNotUseGetters = true ) -public abstract class VdmMediaEntity extends VdmEntity -{ - /** - * Get the binary data stream (file) from this media entity. Perform this operation after retrieving the - * entity object from the OData service. - *

- * Alternatively, you can use this method to only retrieve the media resource without requesting the entity - * data. Build this entity via its {@code .builder()} and use {@link #attachToService(String, Destination)} to - * declare a service path and destination to request the media resource from. You can obtain the service path from - * the {@code #DEFAULT_SERVICE_PATH}, e.g. - * {@code BusinessPartnerServiceBusinessPartnerService.DEFAULT_SERVICE_PATH} - *

- * Please ensure this stream is closed after usage. The below example achieves this using - * try-with-resources: - * - *

-     * try( InputStream content = entity.fetchMediaStream() ) {
-     *     // do something with the content here
-     * }
-     * 
- * - * @return File content as an {@link InputStream}. - * @throws ODataException - * if the request could not be sent or the OData service responded with an error. - */ - @Nonnull - public InputStream fetchMediaStream() - throws ODataException - { - final ODataResourcePath resource = - ODataResourcePath - .of(getEntityCollection(), ODataEntityKey.of(getKey(), ODataProtocol.V2)) - .addSegment("$value"); - - final ODataRequestReadByKey request = - new ODataRequestReadByKey(getServicePathForFetch(), resource, null, ODataProtocol.V2); - - final Destination destination = getDestinationForFetch(); - if( destination == null ) { - throw new ODataRequestException( - request, - "Failed to fetch media stream.", - new IllegalStateException( - "Unable to execute OData query. The entity was created locally without an assigned HttpDestination. This method is applicable only on entities which were retrieved or created using the OData VDM.")); - } - final HttpClient httpClient = ODataApacheHttpClient5Accessor.getHttpClient(destination); - final ODataRequestResultGeneric result = request.execute(httpClient); - @SuppressWarnings( "PMD.CloseResource" ) // entity is intentionally not closed here because its content stream is returned to the caller - final HttpEntity entity = result.getHttpResponse().getEntity(); - if( entity == null ) { - throw new ODataResponseException( - request, - result.getHttpResponse(), - "Failed to read the input stream of the OData response: Response didn't contain any payload.", - null); - } - try { - return entity.getContent(); - } - catch( final IOException | UnsupportedOperationException e ) { - throw new ODataResponseException( - request, - result.getHttpResponse(), - "Failed to read the input stream of the OData response.", - e); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmObject.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmObject.java deleted file mode 100644 index 48549f0e40..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmObject.java +++ /dev/null @@ -1,373 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.sap.cloud.sdk.s4hana.datamodel.odata.exception.NoSuchEntityFieldException; -import com.sap.cloud.sdk.typeconverter.TypeConverter; - -import lombok.EqualsAndHashCode; -import lombok.ToString; - -/** - * Superclass of all entities which contains common elements such as a generic representation of custom fields. - * - * @param - * The type of the implementing object. - */ -@ToString( doNotUseGetters = true ) -@EqualsAndHashCode( doNotUseGetters = true ) -@JsonAutoDetect( - fieldVisibility = JsonAutoDetect.Visibility.ANY, - getterVisibility = JsonAutoDetect.Visibility.NONE, - isGetterVisibility = JsonAutoDetect.Visibility.NONE, - setterVisibility = JsonAutoDetect.Visibility.NONE, - creatorVisibility = JsonAutoDetect.Visibility.NONE ) -public abstract class VdmObject -{ - @JsonIgnore - private final transient Map customFields = new LinkedHashMap<>(); - - /** - * A mapping of the OData field name to the original value. - *

- * This should be updated via {@link #rememberChangedField(String, Object)} on every set call of a property. - */ - @JsonIgnore - @Nonnull - protected final transient Map changedOriginalFields = new HashMap<>(); - - /** - * Returns the names of all custom fields of this object. - * - * @return The names of the custom fields of this object. - */ - @Nonnull - public Set getCustomFieldNames() - { - return customFields.keySet(); - } - - /** - * Returns all custom field names and values of this object. - * - * @return All names & values of custom fields as a map. - */ - @JsonAnyGetter - @Nonnull - public Map getCustomFields() - { - return customFields; - } - - /** - * Sets the value of a single custom field. - * - * @param customFieldName - * Name of the custom field. - * @param value - * Value of the custom field. - */ - @JsonAnySetter - public void setCustomField( @Nonnull final String customFieldName, @Nullable final Object value ) - { - rememberChangedField(customFieldName, customFields.get(customFieldName)); - customFields.put(customFieldName, value); - } - - /** - * Sets the value of a single custom field. If the EntityField passed as parameter holds a TypeConverter, the value - * will be converted before it's stored. - * - * @param customField - * Name of the custom field, represented as an EntityField object. - * @param value - * Value of the custom field. - * @param - * The type of the custom field to set. - */ - public void setCustomField( - @Nonnull final EntityField customField, - @Nullable final FieldT value ) - { - if( customField.getTypeConverter() != null ) { - setCustomField(customField.getFieldName(), customField.getTypeConverter().toDomain(value).get()); - } else { - setCustomField(customField.getFieldName(), value); - } - } - - /** - * Checks whether this object contains a custom field with the given name. - * - * @param customFieldName - * Name of the custom field to check for - * - * @return {@code true} if this entity has a custom field with the given name, {@code false} otherwise. - */ - public boolean hasCustomField( @Nonnull final String customFieldName ) - { - return customFields.containsKey(customFieldName); - } - - /** - * Checks whether this object contains a value for the given custom field. - * - * @param customField - * Custom field to check for, represented as an {@code EntityField} object. - * - * @return {@code true} if this object has a custom field with the name of the given field, {@code false} otherwise. - */ - public boolean hasCustomField( @Nonnull final EntityField customField ) - { - return hasCustomField(customField.getFieldName()); - } - - /** - * This method allows for retrieval of custom fields that are added to the underlying OData services. - * - * @param customFieldName - * Name of the field returned by the underlying OData service. - * @param - * The type of the returned field. - * - * @return The value of the custom field. Actual type will depend on the type configured in the underlying OData - * service. - * - * @throws NoSuchEntityFieldException - * if no field with the given name could be found. - */ - @SuppressWarnings( "unchecked" ) - @Nullable - public FieldT getCustomField( @Nonnull final String customFieldName ) - throws NoSuchEntityFieldException - { - if( !hasCustomField(customFieldName) ) { - throw new NoSuchEntityFieldException("Object has no field with name '" + customFieldName + "'."); - } - return (FieldT) customFields.get(customFieldName); - } - - @SuppressWarnings( "unchecked" ) - @Nullable - private FieldT getCustomField( - @Nonnull final String customFieldName, - @Nonnull final TypeConverter typeConverter ) - throws NoSuchEntityFieldException - { - if( !hasCustomField(customFieldName) ) { - throw new NoSuchEntityFieldException("Object has no field with name '" + customFieldName + "'."); - } - return typeConverter.fromDomain((T) customFields.get(customFieldName)).get(); - } - - /** - * This method allows for retrieval of custom fields that are added to the underlying OData services. If the - * EntityField passed as parameter holds a TypeConverter, the value will be converted before it's returned. - * - * @param customField - * Field returned by the underlying OData service. - * @param - * The type of the returned field. - * - * @return The value of the custom field. Actual type will depend on the type configured in the underlying OData - * service. - * - * @throws NoSuchEntityFieldException - * if no field with the given name could be found. - */ - @Nullable - public FieldT getCustomField( @Nonnull final EntityField customField ) - throws NoSuchEntityFieldException - { - if( customField.getTypeConverter() != null ) { - return getCustomField(customField.getFieldName(), customField.getTypeConverter()); - } else { - return getCustomField(customField.getFieldName()); - } - } - - /** - * Returns the class of this object. - * - * @return The class of this object. - */ - @Nonnull - public abstract Class getType(); - - /** - * Returns the compound key of this object. - * - * @return The compound key of this object. - */ - @Nonnull - protected Map getKey() - { - return new HashMap<>(); - } - - /** - * Sets the values of all custom fields contained in the given {@code values}. - *

- * Afterwards, marks all fields as unchanged. - *

- * - * @param values - * The map of custom fields to set. - */ - protected void fromMap( final Map values ) - { - for( final Map.Entry entry : values.entrySet() ) { - setCustomField(entry.getKey(), entry.getValue()); - } - - resetChangedFields(); - } - - /** - * Returns a map of all custom fields contained in this object. - * - * @return A map of all custom fields contained in this object. - */ - @Nonnull - protected Map toMapOfCustomFields() - { - return Maps.newHashMap(getCustomFields()); - } - - /** - * Returns a set of all custom field names contained in this object. - * - * @return A set of all custom field names contained in this object. - */ - @Nonnull - protected Set getSetOfCustomFields() - { - return Sets.newHashSet(getCustomFields().keySet()); - } - - /** - * Returns a map of all fields contained in this object. - * - * @return A map of all fields contained in this object. - */ - @Nonnull - protected Map toMapOfFields() - { - return new HashMap<>(); - } - - /** - * Returns a set of all field names contained in this object. - * - * @return A set of all field names contained in this object. - */ - @Nonnull - protected Set getSetOfFields() - { - return Sets.newHashSet(toMapOfFields().keySet()); - } - - /** - * Returns a map of all navigation properties contained in this object. - * - * @return A map of all navigation properties contained in this object. - */ - @Nonnull - protected Map toMapOfNavigationProperties() - { - return new HashMap<>(); - } - - /** - * Returns a set of all navigation property names contained in this object. - * - * @return A set of all navigation property names contained in this object. - */ - @Nonnull - protected Set getSetOfNavigationProperties() - { - return Sets.newHashSet(toMapOfNavigationProperties().keySet()); - } - - /** - * Returns a map of all fields, navigation properties, and custom fields contained in this object. - * - * @return A map of all fields, navigation properties, and custom fields contained in this object. - */ - @Nonnull - protected Map toMap() - { - final Map values = new HashMap<>(); - - values.putAll(toMapOfFields()); - values.putAll(toMapOfNavigationProperties()); - values.putAll(toMapOfCustomFields()); - - return values; - } - - /** - * Returns map of all fields which have been changed on this entity along with their updated values. - * - * @return Map containing all changed fields with their current value. - */ - @Nonnull - public Map getChangedFields() - { - final Map changedFields = new HashMap<>(); - - final Map currentFields = toMapOfFields(); - currentFields.putAll(getCustomFields()); - - for( final Map.Entry changedOriginalField : changedOriginalFields.entrySet() ) { - final Object originalValue = changedOriginalField.getValue(); - final Object currentValue = currentFields.get(changedOriginalField.getKey()); - - if( originalValue != null && !originalValue.equals(currentValue) - || originalValue == null && currentValue != null ) { - - changedFields.put(changedOriginalField.getKey(), currentValue); - } - } - - return changedFields; - } - - /** - * Remembers the original value of a changed field. - * - * @param fieldName - * The name of the field that is changed. - * @param valueBeforeChange - * The original value before the change. - */ - protected void rememberChangedField( @Nonnull final String fieldName, @Nullable final Object valueBeforeChange ) - { - if( !changedOriginalFields.containsKey(fieldName) ) { - changedOriginalFields.put(fieldName, valueBeforeChange); - } - } - - /** - * Resets the map of all fields which have been changed on this entity. - *

- * After calling this method, no field is considered changed, until you change the value of fields on this entity - * afterwards. - */ - public void resetChangedFields() - { - changedOriginalFields.clear(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchChangeSetFluentHelperBasic.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchChangeSetFluentHelperBasic.java deleted file mode 100644 index d19638cc57..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchChangeSetFluentHelperBasic.java +++ /dev/null @@ -1,216 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestAction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCreate; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestDelete; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestUpdate; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperDelete; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperUpdate; -import com.sap.cloud.sdk.datamodel.odata.helper.VdmEntity; - -import lombok.RequiredArgsConstructor; - -/** - * Representation of any changeset in a OData batch request as a fluent interface. - * - * @param - * The fluent helper type of the associated OData batch request. - * @param - * The current type of the implementing fluent helper instance. - */ -@RequiredArgsConstructor -public abstract class BatchChangeSetFluentHelperBasic, ThisT> - implements - FluentHelperBatchEndChangeSet -{ - private final FluentHelperBatchT typedBatchFluentHelper; - private final BatchFluentHelperBasic basicBatchFluentHelper; - - private final List operations = new ArrayList<>(); - - /** - * Method to safely return the current fluent helper instance upon public method calls. - * - * @return The current fluent helper instance. - */ - @Nonnull - protected abstract ThisT getThis(); - - /** - * Add a delete operation to the current changeset request. - * - * @param serviceMethod - * The method of a delete fluent helper from the given service class. - * @param entity - * The entity instance to be deleted. - * @param - * The entity type to guarantee type safety. - * @return The current fluent helper instance. - */ - @Nonnull - protected > ThisT addRequestDelete( - final Function> serviceMethod, - final EntityT entity ) - { - final FluentHelperDelete delete = serviceMethod.apply(entity); - return addRequest(delete); - } - - /** - * Adds a delete operation to the current changeset request. - * - * @param deleteRequest - * The {@link FluentHelperDelete} that represents the delete operation. - * @return The current fluent helper instance. - */ - @Nonnull - protected ThisT addRequest( @Nonnull final FluentHelperDelete deleteRequest ) - { - final ODataRequestDelete request = deleteRequest.toRequest(); - basicBatchFluentHelper.requestMapping.put(deleteRequest, request); - - final BatchRequestChangeSetOperation operation = - new BatchRequestChangeSetOperation(( changeSet ) -> changeSet.addDelete(request), deleteRequest); - - operations.add(operation); - return getThis(); - } - - /** - * Add a create operation to the current changeset request. - * - * @param serviceMethod - * The method of a create fluent helper from the given service class. - * @param entity - * The entity instance to be created. - * @param - * The entity type to guarantee type safety. - * @return The current fluent helper instance. - */ - @Nonnull - protected > ThisT addRequestCreate( - final Function> serviceMethod, - final EntityT entity ) - { - final FluentHelperCreate create = serviceMethod.apply(entity); - return addRequest(create); - } - - /** - * Adds a create operation to the current changeset request. - * - * @param createRequest - * The {@link FluentHelperCreate} that represents the create operation. - * @return The current fluent helper instance. - */ - @Nonnull - protected ThisT addRequest( @Nonnull final FluentHelperCreate createRequest ) - { - final ODataRequestCreate request = createRequest.toRequest(); - basicBatchFluentHelper.requestMapping.put(createRequest, request); - - final BatchRequestChangeSetOperation operation = - new BatchRequestChangeSetOperation(( changeSet ) -> changeSet.addCreate(request), createRequest); - - operations.add(operation); - return getThis(); - } - - /** - * Add an update operation to the current changeset request. - * - * @param serviceMethod - * The method of an update fluent helper from the given service class. - * @param entity - * The entity instance to be updated. - * @param - * The entity type to guarantee type safety. - * @return The current fluent helper instance. - */ - @Nonnull - protected > ThisT addRequestUpdate( - final Function> serviceMethod, - final EntityT entity ) - { - final FluentHelperUpdate update = serviceMethod.apply(entity); - return addRequest(update); - } - - /** - * Adds an update operation to the current changeset request. - * - * @param updateRequest - * The {@link FluentHelperUpdate} that represents the update operation. - * @return The current fluent helper instance. - */ - @Nonnull - protected ThisT addRequest( @Nonnull final FluentHelperUpdate updateRequest ) - { - final ODataRequestUpdate request = updateRequest.toRequest(); - basicBatchFluentHelper.requestMapping.put(updateRequest, request); - - final BatchRequestChangeSetOperation operation = - new BatchRequestChangeSetOperation(( changeSet ) -> changeSet.addUpdate(request), updateRequest); - - operations.add(operation); - return getThis(); - } - - @Nonnull - @Override - public FluentHelperBatchT endChangeSet() - { - final BatchRequestChangeSet changeSet = new BatchRequestChangeSet(operations); - basicBatchFluentHelper.addChangeSet(changeSet); - return typedBatchFluentHelper; - } - - /** - * Adds a function import call to the currently opened changeset. Only use {@code executeRequest} to issue the batch - * request. - * - * @param functionImport - * The {@link FluentHelperFunction} that represents the function import call - * @return The same fluent helper - * @throws IllegalStateException - * If the batch request contains a function import call within a change set which uses the HTTP GET - * method. - */ - @Nonnull - public ThisT addFunctionImport( @Nonnull final FluentHelperFunction functionImport ) - { - final ODataRequestGeneric request = functionImport.toRequest(); - basicBatchFluentHelper.requestMapping.put(functionImport, request); - - final Consumer changesetConsumer; - - if( request instanceof ODataRequestAction ) { - changesetConsumer = changeset -> changeset.addAction((ODataRequestAction) request); - } else { - throw new IllegalStateException( - "Request for function imports in batch change sets must be " - + ODataRequestAction.class.getSimpleName() - + ", but was " - + request.getClass().getSimpleName() - + ". Only function imports using HTTP POST are allowed."); - } - - final BatchRequestChangeSetOperation operation = - new BatchRequestChangeSetOperation(changesetConsumer, functionImport); - - operations.add(operation); - - return getThis(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchFluentHelperBasic.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchFluentHelperBasic.java deleted file mode 100644 index e3fc705f30..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchFluentHelperBasic.java +++ /dev/null @@ -1,219 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import java.util.ArrayList; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.Supplier; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestAction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestFunction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestReadByKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultMultipartGeneric; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperBasic; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperByKey; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperDelete; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperModification; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperRead; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperUpdate; - -import lombok.AccessLevel; -import lombok.Getter; - -/** - * Representation of any OData batch request as a fluent interface for managing changesets and - * {@link #executeRequest(Destination) executing} them in a single query. - * - * @param - * The fluent helper type. - * @param - * The type of the changesets being managed in this OData batch request. - */ -public abstract class BatchFluentHelperBasic, FluentHelperBatchChangeSetT extends FluentHelperBatchEndChangeSet> - implements - FluentHelperServiceBatch -{ - @Getter( AccessLevel.PACKAGE ) - private final List requestParts = new ArrayList<>(); - final Map, Integer> requestMappingLegacy = new IdentityHashMap<>(); - @Getter( AccessLevel.PACKAGE ) - final Map, ODataRequestGeneric> requestMapping = new IdentityHashMap<>(); - - Supplier uuidProvider = UUID::randomUUID; - - private boolean skipCsrfTokenRetrieval = false; - - /** - * Get the OData service endpoint path for the current OData batch request. Usually it can be found as static member - * DEFAULT_SERVICE_PATH in the service class. - * - * @return The String representation of the OData service endpoint path. - */ - @Nonnull - protected abstract String getServicePathForBatchRequest(); - - /** - * Method to safely return the current fluent helper instance upon public method calls. - * - * @return The current fluent helper instance. - */ - @Nonnull - protected abstract FluentHelperBatchT getThis(); - - @Nonnull - @Override - public BatchResponse executeRequest( @Nonnull final Destination destination ) - { - final HttpClient httpClient = - skipCsrfTokenRetrieval - ? ODataApacheHttpClient5Accessor.getHttpClientWithoutCsrf(destination) - : ODataApacheHttpClient5Accessor.getHttpClient(destination); - - @SuppressWarnings( "PMD.CloseResource" ) // The ODataRequestResultMultipartGeneric is closed by DefaultBatchResponseResult - final ODataRequestResultMultipartGeneric result = toRequest().execute(httpClient); - - return DefaultBatchResponseResult.of(requestParts, requestMapping, result); - } - - /** - * Translate this OData v2 batch request into a generic {@link ODataRequestBatch}. - * - * @return A protocol agnostic OData batch request instance. - */ - @Nonnull - public ODataRequestBatch toRequest() - { - final String servicePath = getServicePathForBatchRequest(); - final ODataRequestBatch requestBatch = new ODataRequestBatch(servicePath, ODataProtocol.V2, uuidProvider); - - for( final BatchRequestOperation part : requestParts ) { - part.addToRequestBuilder(requestBatch); - } - return requestBatch; - } - - /** - * Deactivates the CSRF token retrieval for this OData request. This is useful if the server does not support or - * require CSRF tokens as part of the request. - * - * @return The same builder - */ - @Nonnull - public FluentHelperBatchT withoutCsrfToken() - { - skipCsrfTokenRetrieval = true; - return getThis(); - } - - /** - * Method handler to register a finished changeset definition. - * - * @param changeSet - * Instance of the changeset, containing OData operations modifying entities. - */ - void addChangeSet( @Nonnull final BatchRequestChangeSet changeSet ) - { - requestParts.add(changeSet); - } - - @Nonnull - @Override - public FluentHelperBatchT addReadOperations( @Nonnull final FluentHelperRead... readOperations ) - { - for( final FluentHelperRead operation : readOperations ) { - final ODataRequestRead request = operation.toRequest(); - requestMappingLegacy.put(operation, requestParts.size()); - requestMapping.put(operation, request); - requestParts.add(new BatchRequestRead.GetAll(operation, request)); - } - return getThis(); - } - - @Nonnull - @Override - public FluentHelperBatchT addReadOperations( @Nonnull final FluentHelperByKey... readByKeyOperations ) - { - for( final FluentHelperByKey operation : readByKeyOperations ) { - final ODataRequestReadByKey request = operation.toRequest(); - requestMappingLegacy.put(operation, requestParts.size()); - requestMapping.put(operation, request); - requestParts.add(new BatchRequestRead.GetByKey(operation, request)); - } - return getThis(); - } - - @Nonnull - @Override - public FluentHelperBatchT addReadOperations( @Nonnull final FluentHelperFunction... functionOperations ) - { - for( final FluentHelperFunction operation : functionOperations ) { - final ODataRequestGeneric request = operation.toRequest(); - if( request instanceof ODataRequestAction ) { - throw new IllegalStateException( - "Request for function imports while adding read operations must be " - + ODataRequestFunction.class.getSimpleName() - + ", but was " - + request.getClass().getSimpleName() - + ". Only function imports using HTTP GET are allowed."); - } - requestMappingLegacy.put(operation, requestParts.size()); - requestMapping.put(operation, request); - requestParts.add(new BatchRequestRead.GetFunctionRequest(operation, (ODataRequestFunction) request)); - } - return getThis(); - } - - @Nonnull - @Override - public FluentHelperBatchT addChangeSet( @Nonnull final FluentHelperModification... modifications ) - { - final FluentHelperBatchChangeSetT changeSet = beginChangeSet(); - - if( !(changeSet instanceof BatchChangeSetFluentHelperBasic) ) { - throw new UnsupportedOperationException( - String - .format( - "%1$s::beginChangeSet does not return an instance of type %2$s. To fix this issue, you may either implement %1$s::addChangeSet yourself, or use the default implementation of %3$s::beginChangeSet.", - getClass().getName(), - BatchChangeSetFluentHelperBasic.class.getName(), - BatchFluentHelperBasic.class.getName())); - } - - final BatchChangeSetFluentHelperBasic mutableChangeSet = - (BatchChangeSetFluentHelperBasic) changeSet; - - for( final FluentHelperModification modification : modifications ) { - final ODataRequestGeneric request = modification.toRequest(); - requestMapping.put(modification, request); - requestMappingLegacy.put(modification, requestMappingLegacy.size()); - - if( modification instanceof FluentHelperCreate ) { - mutableChangeSet.addRequest((FluentHelperCreate) modification); - } else if( modification instanceof FluentHelperUpdate ) { - mutableChangeSet.addRequest((FluentHelperUpdate) modification); - } else if( modification instanceof FluentHelperDelete ) { - mutableChangeSet.addRequest((FluentHelperDelete) modification); - } else { - throw new IllegalArgumentException( - "Failed to add unknown type of modifying operation to OData batch request: " - + modification.getClass().getSimpleName()); - } - } - - changeSet.endChangeSet(); - return getThis(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestChangeSet.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestChangeSet.java deleted file mode 100644 index d92bbdbdfd..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestChangeSet.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import java.util.List; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * Instance of a OData batch request changeset, defined by one or many OData operations. - */ -@RequiredArgsConstructor -class BatchRequestChangeSet implements BatchRequestOperation -{ - @Getter( AccessLevel.PACKAGE ) - @Nonnull - private final List operations; - - @Override - public void addToRequestBuilder( @Nonnull final ODataRequestBatch requestBatch ) - { - final ODataRequestBatch.Changeset requestChangeset = requestBatch.beginChangeset(); - for( final BatchRequestChangeSetOperation operation : getOperations() ) { - operation.getChangeSetConsumer().accept(requestChangeset); - } - requestChangeset.endChangeset(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestChangeSetOperation.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestChangeSetOperation.java deleted file mode 100644 index f5bc12d6b3..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestChangeSetOperation.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import java.util.function.Consumer; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperBasic; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -@Getter( AccessLevel.PACKAGE ) -class BatchRequestChangeSetOperation -{ - private final Consumer changeSetConsumer; - private final FluentHelperBasic fluentHelper; -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestOperation.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestOperation.java deleted file mode 100644 index 860d7bf8c6..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestOperation.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; - -/** - * Common interface for items on root level of an OData batch request. - */ -interface BatchRequestOperation -{ - void addToRequestBuilder( @Nonnull final ODataRequestBatch requestBatch ); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestRead.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestRead.java deleted file mode 100644 index 61c115a5f1..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchRequestRead.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestFunction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestReadByKey; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperByKey; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperRead; - -import lombok.EqualsAndHashCode; -import lombok.Value; - -abstract class BatchRequestRead implements BatchRequestOperation -{ - @Value - @EqualsAndHashCode( callSuper = true ) - static class GetAll extends BatchRequestRead - { - FluentHelperRead fluentHelper; - ODataRequestRead request; - - @Override - public void addToRequestBuilder( @Nonnull final ODataRequestBatch builder ) - { - builder.addRead(request); - } - } - - @Value - @EqualsAndHashCode( callSuper = true ) - static class GetByKey extends BatchRequestRead - { - FluentHelperByKey fluentHelper; - ODataRequestReadByKey request; - - @Override - public void addToRequestBuilder( @Nonnull final ODataRequestBatch builder ) - { - builder.addReadByKey(request); - } - } - - @Value - @EqualsAndHashCode( callSuper = true ) - static class GetFunctionRequest extends BatchRequestRead - { - FluentHelperFunction fluentHelper; - ODataRequestFunction request; - - @Override - public void addToRequestBuilder( @Nonnull final ODataRequestBatch builder ) - { - builder.addFunction(request); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchResponse.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchResponse.java deleted file mode 100644 index 43b0b4f864..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchResponse.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import java.util.List; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.helper.CollectionValuedFluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperByKey; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperRead; -import com.sap.cloud.sdk.datamodel.odata.helper.SingleValuedFluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.VdmEntity; - -import io.vavr.control.Try; - -/** - * Interface to access the OData batch response. - */ -public interface BatchResponse extends AutoCloseable -{ - /** - * Get the result for a single changeset. - * - * @param index - * The zero-based index of the selected changeset. - * @return A wrapper of the changeset result. It can be checked for errors upon evaluation. - */ - @Nonnull - Try get( int index ); - - /** - * Convenience method to get the result for a read request on the OData batch response. - * - * @param helper - * The original fluent helper instance that was used to create the request. - * @param - * The generic entity type. - * @return A list of entities according to the original request. - */ - @Nonnull - default > List getReadResult( - @Nonnull final FluentHelperRead helper ) - { - throw new UnsupportedOperationException(); - } - - /** - * Convenience method to get the result for a read-by-key request on the OData batch response. - * - * @param helper - * The original fluent helper instance that was used to create the request. - * @param - * The generic entity type. - * @return A single entity according to the original request. - */ - @Nonnull - default < - EntityT extends VdmEntity> EntityT getReadResult( @Nonnull final FluentHelperByKey helper ) - { - throw new UnsupportedOperationException(); - } - - /** - * Convenience method to get the result for a function import request that returns a single primitive value or - * entity on the OData batch response. - * - * @param helper - * The original fluent helper instance that was used to create the request. - * @param - * The result type of the function import request - * @return A single primitive value or entity according to the original request - */ - @Nonnull - default ResultT getReadResult( @Nonnull final SingleValuedFluentHelperFunction helper ) - { - throw new UnsupportedOperationException(); - } - - /** - * Convenience method to get the result for a function import request that returns a collection of primitive values - * or entities on the OData batch response. - * - * @param helper - * The original fluent helper instance that was used to create the request. - * @param - * The result type of the function import request - * @return A collection of primitive values or entities according to the original request - */ - @Nonnull - default List getReadResult( - @Nonnull final CollectionValuedFluentHelperFunction helper ) - { - throw new UnsupportedOperationException(); - } - - /** - * Closes the underlying HTTP response entity. - * - * @since 4.15.0 - */ - @Override - void close(); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchResponseChangeSet.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchResponseChangeSet.java deleted file mode 100644 index 36c20acbbe..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchResponseChangeSet.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import java.util.List; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.helper.VdmEntity; - -/** - * Interface to evaluate the response of a single changeset from an OData batch response. - */ -public interface BatchResponseChangeSet -{ - /** - * Get all newly created entities from this changeset. - * - * @return A list of generic {@link VdmEntity} instances. The consumer can type-check, evaluate and cast its - * entries. - */ - @Nonnull - List> getCreatedEntities(); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchService.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchService.java deleted file mode 100644 index 8c61d0f23c..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/BatchService.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import javax.annotation.Nonnull; - -/** - * Interface to expose the batch feature for service class. - * - * @param - * The type of the Batch instance. - */ -public interface BatchService -{ - /** - * Instantiate a new FluentHelper instance for a single OData batch request. - * - * @return A new instance of an OData batch request associated with the service object. - */ - @Nonnull - FluentHelperBatchT batch(); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/DefaultBatchResponseChangeSet.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/DefaultBatchResponseChangeSet.java deleted file mode 100644 index 648d19620d..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/DefaultBatchResponseChangeSet.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import java.util.List; -import java.util.function.Function; -import java.util.stream.Collectors; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate; -import com.sap.cloud.sdk.datamodel.odata.helper.VdmEntity; - -import lombok.Value; - -@Value -class DefaultBatchResponseChangeSet implements BatchResponseChangeSet -{ - List changesetOperations; - Function> changesetEntityExtractor; - - @Nonnull - @Override - public List> getCreatedEntities() - { - return changesetOperations - .stream() - .filter(req -> req.getFluentHelper() instanceof FluentHelperCreate) - .map(changesetEntityExtractor) - .collect(Collectors.toList()); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/DefaultBatchResponseResult.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/DefaultBatchResponseResult.java deleted file mode 100644 index 1dafddcc88..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/DefaultBatchResponseResult.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultMultipartGeneric; -import com.sap.cloud.sdk.datamodel.odata.helper.CollectionValuedFluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperBasic; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperByKey; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperRead; -import com.sap.cloud.sdk.datamodel.odata.helper.SingleValuedFluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.VdmEntity; -import com.sap.cloud.sdk.datamodel.odata.helper.VdmEntityUtil; - -import io.vavr.control.Try; -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -/** - * Generic OData service response wrapper for Batch response. - * - */ -@Slf4j -@RequiredArgsConstructor( staticName = "of", access = AccessLevel.PACKAGE ) -public class DefaultBatchResponseResult implements BatchResponse -{ - private final List requestParts; - private final Map, ODataRequestGeneric> requestMapping; - private final ODataRequestResultMultipartGeneric result; - - /** - * Static factory method to convert from generic response to typed response. - * - * @param response - * The generic response that should be converted. - * @param initialRequest - * The initial BatchRequest - * @return The typed (high-level) BatchResponse object. - */ - @Nonnull - public static DefaultBatchResponseResult of( - @Nonnull final ODataRequestResultMultipartGeneric response, - @Nonnull final BatchFluentHelperBasic initialRequest ) - { - return DefaultBatchResponseResult - .of(initialRequest.getRequestParts(), initialRequest.getRequestMapping(), response); - } - - @Nonnull - @Override - public Try get( final int index ) - { - final BatchRequestChangeSet requests = getRequestPartByTypeAndIndex(BatchRequestChangeSet.class, index); - if( requests == null ) { - final String errorMessage = "Unable to find changeset " + index + " in batch request."; - return Try.failure(new IllegalArgumentException(errorMessage)); - } - - // perform health check for first operation response in changeset - final FluentHelperBasic vdmRequest = requests.getOperations().get(0).getFluentHelper(); - final ODataRequestGeneric request = requestMapping.get(vdmRequest); - final Try testParse = Try.run(() -> result.getResult(request)); - if( testParse.isFailure() ) { - return Try.failure(testParse.getCause()); - } - - return Try.success(new DefaultBatchResponseChangeSet(requests.getOperations(), this::getResultingEntity)); - } - - @Nonnull - private VdmEntity getResultingEntity( @Nonnull final BatchRequestChangeSetOperation req ) - { - final ODataRequestGeneric request = requestMapping.get(req.getFluentHelper()); - final Class> entityClass = VdmEntityUtil.getEntityClass(req.getFluentHelper()); - return result.getResult(request).as(entityClass); - } - - @Nonnull - @Override - public > List getReadResult( - @Nonnull final FluentHelperRead helper ) - { - final Class entityClass = VdmEntityUtil.getEntityClass(helper); - return result.getResult(requestMapping.get(helper)).asList(entityClass); - } - - @Nonnull - @Override - public < - EntityT extends VdmEntity> EntityT getReadResult( @Nonnull final FluentHelperByKey helper ) - { - final Class entityClass = VdmEntityUtil.getEntityClass(helper); - return result.getResult(requestMapping.get(helper)).as(entityClass); - } - - @Nonnull - @Override - public ResultT getReadResult( @Nonnull final SingleValuedFluentHelperFunction helper ) - { - final Class resultClass = VdmEntityUtil.getEntityClass(helper); - return result.getResult(requestMapping.get(helper)).as(resultClass); - } - - @Nonnull - @Override - public List getReadResult( - @Nonnull final CollectionValuedFluentHelperFunction helper ) - { - final Class resultClass = VdmEntityUtil.getEntityClass(helper); - return result.getResult(requestMapping.get(helper)).asList(resultClass); - } - - @Nullable - private T getRequestPartByTypeAndIndex( final Class type, final int index ) - { - return requestParts.stream().filter(type::isInstance).map(type::cast).skip(index).findFirst().orElse(null); - } - - /** - * Closes the underlying HTTP response entity. - * - * @since 4.15.0 - */ - @Override - public void close() - { - result.close(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperBatchChangeSet.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperBatchChangeSet.java deleted file mode 100644 index 4bf49c48e9..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperBatchChangeSet.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperFunction; - -/** - * Contains methods applicable to enrich the currently open change set. - * - * @param - * The same fluent helper - */ -public interface FluentHelperBatchChangeSet -{ - /** - * Adds a function import call to the currently opened changeset. Only use {@code executeRequest} to issue the batch - * request. - * - * @param functionImport - * The {@link FluentHelperFunction} that represents the function import call - * @return The same fluent helper - * @throws IllegalStateException - * If the batch request contains a function import call within a change set which uses the HTTP GET - * method. - */ - @Nonnull - FluentHelperT addFunctionImport( @Nonnull final FluentHelperFunction functionImport ); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperBatchEndChangeSet.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperBatchEndChangeSet.java deleted file mode 100644 index 1d25c30f66..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperBatchEndChangeSet.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import javax.annotation.Nonnull; - -/** - * Interface to finish the definition of a single changeset. - * - * @param - * Type of the implementing OData batch request class - */ -public interface FluentHelperBatchEndChangeSet -{ - /** - * Finish the definition of a single changeset and return to the parent OData batch request instance. All changesets - * will be evaluated independently from each other. - * - * @return The current OData batch request instance. - */ - @Nonnull - FluentHelperT endChangeSet(); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperServiceBatch.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperServiceBatch.java deleted file mode 100644 index ba6f123deb..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperServiceBatch.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperByKey; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperModification; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperRead; - -/** - * Interface to the batch object of an OData service. - * - * @param - * Type of the implementing OData batch request class - * @param - * Type of the associated changeset class - */ -public interface FluentHelperServiceBatch> - extends - FluentHelperServiceBatchExecute -{ - /** - * Method to define a new OData batch changeset. Modifying operations on entities in a batch request can only be - * done as part of a changeset. All changesets will be evaluated independently from each other. - * - * @return A new instance of a batch changeset associated with the OData service. - */ - @Nonnull - FluentHelperChangeSetT beginChangeSet(); - - /** - * Add read request to the OData batch request builder. - * - * @param readOperations - * A var-arg array of read operations. - * @return The current OData batch request instance. - */ - @Nonnull - FluentHelperT addReadOperations( @Nonnull final FluentHelperRead... readOperations ); - - /** - * Adds a single OData batch changeset that includes all of the given data modification requests. All changesets - * will be evaluated independently from each other. - * - * @param modifications - * The data modification requests to be performed as part of a single changeset. - * @return The current OData batch request instance. - */ - @Nonnull - FluentHelperT addChangeSet( @Nonnull final FluentHelperModification... modifications ); - - /** - * Add read-by-key request to the OData batch request builder. - * - * @param readByKeyOperations - * A var-arg array of read operations. - * @return The current OData batch request instance. - */ - @Nonnull - FluentHelperT addReadOperations( @Nonnull final FluentHelperByKey... readByKeyOperations ); - - /** - * Add function requests to the OData batch request builder.Only functions that use GET can be added. - * - * @param functionOperations - * A var-arg array of function operations. - * @return The current OData batch request instance - */ - @Nonnull - FluentHelperT addReadOperations( @Nonnull final FluentHelperFunction... functionOperations ); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperServiceBatchExecute.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperServiceBatchExecute.java deleted file mode 100644 index a832209529..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/FluentHelperServiceBatchExecute.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; - -/** - * Interface to provide execute methods. - * - */ -public interface FluentHelperServiceBatchExecute -{ - /** - * Executes the underlying batch query including the stored changeset operations. - * - * @param destination - * Destination object for resolving the {@code HttpClient} when executing the underlying OData query. - * - * @return A single result element, holding each changeset response. - */ - @Nonnull - BatchResponse executeRequest( @Nonnull final Destination destination ); -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/AbstractCalendarAdapter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/AbstractCalendarAdapter.java deleted file mode 100644 index a33dbb04b5..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/AbstractCalendarAdapter.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.io.IOException; -import java.util.Calendar; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -/** - * Abstract base class to be used to easily parse fields which can be read as a {@link Calendar} object as a Gson - * {@link TypeAdapter}. - *

- * This may be used to specify an adapter for the new Java date API (e.g. {@link java.time.LocalDateTime}) based on a - * common conversion logic. This way the logic is split the following way: - * - * - * - * - * - * - * - * - * - * - * - * - * - *
General Conversion logicAbstractTypeConverter subclasses (*CalendarConverter)
Gson AdapterAbstractCalendarAdapter subclasses
Jackson (De)SerializerAbstractJacksonCalendar(De)Serializer and subclasses
- * - * @param - * The type this adapter should parse. - */ -public abstract class AbstractCalendarAdapter extends TypeAdapter -{ - /** - * Converts a string value read from a json property as an instance of the type to be created by this adapter. - * - * @param jsonString - * The string value of a json property. - * @return A {@code ConvertedObject} instance containing the resulting object, or is empty if conversion was not - * possible. - */ - @Nonnull - protected abstract ConvertedObject convertStringToType( @Nonnull final String jsonString ); - - /** - * Converts an instance of the type handled by this adapter into a string that can be written as a json value. - * - * @param entity - * The entity to convert into a String - * @return A {@code ConvertedObject} instance containing the resulting string, or is empty if conversion was not - * possible. - */ - @Nonnull - protected abstract ConvertedObject convertTypeToString( @Nullable final T entity ); - - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final T value ) - throws IOException - { - final ConvertedObject maybeString = convertTypeToString(value); - if( maybeString.isConvertible() ) { - out.value(maybeString.get()); - } - } - - @Override - @Nullable - public T read( @Nonnull final JsonReader jsonReader ) - throws IOException - { - if( jsonReader.peek() != JsonToken.STRING ) { - jsonReader.skipValue(); - return null; - } - - final String jsonValue = jsonReader.nextString(); - - return convertStringToType(jsonValue).orNull(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/AbstractJacksonCalendarDeserializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/AbstractJacksonCalendarDeserializer.java deleted file mode 100644 index eaefefcddf..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/AbstractJacksonCalendarDeserializer.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.io.IOException; -import java.util.Calendar; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -/** - * Abstract base class to be used to easily read fields which can be read as a {@link Calendar} object as a Jackson - * {@link StdDeserializer}. - *

- * This may be used to specify a deserializer for the new Java date API (e.g. {@link java.time.LocalDateTime}) based on - * a common conversion logic. This way the logic is split the following way: - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - *
General Conversion logicAbstractTypeConverter subclasses (*CalendarConverter)
Gson AdapterAbstractCalendarAdapter subclasses
Jackson (De)SerializerAbstractJacksonCalendar(De)Serializer and subclasses
- *

- * - * @param - * The type this deserializer should read. - */ -public abstract class AbstractJacksonCalendarDeserializer extends StdDeserializer -{ - private static final long serialVersionUID = -3931503330406820250L; - - /** - * Constructor needed by the super class. - * - * @param vc - * The class to be read by this deserializer. - */ - protected AbstractJacksonCalendarDeserializer( final Class vc ) - { - super(vc); - } - - /** - * Getter for an instance of the common conversion logic from and to {@code Calendar}. - * - * @return The conversion logic for this deserializer. - */ - @Nonnull - protected abstract AbstractTypeConverter getCalendarConverterInstance(); - - /** - * Getter for the conversion of a String into a Calendar object. The selection of implementation depends on what - * kind of date object the string value should represent. - * - * @return {@code AbstractTypeConverter} instance that converts a String into a Calendar. - */ - @Nonnull - protected abstract AbstractTypeConverter getStringCalendarConverterInstance(); - - @Override - @Nullable - public T deserialize( - @Nonnull final JsonParser jsonParser, - @Nonnull final DeserializationContext deserializationContext ) - throws IOException - { - final ConvertedObject cal = getStringCalendarConverterInstance().toDomain(jsonParser.getText()); - - try { - return getCalendarConverterInstance().fromDomain(cal.orNull()).orNull(); - } - catch( final Exception e ) { - throw new IOException("Could not convert the read Calendar: " + cal, e); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/AbstractJacksonCalendarSerializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/AbstractJacksonCalendarSerializer.java deleted file mode 100644 index b2ff89fd5e..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/AbstractJacksonCalendarSerializer.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.io.IOException; -import java.util.Calendar; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.fasterxml.jackson.databind.ser.std.StringSerializer; -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -/** - * Abstract base class to be used to easily write fields which can be read as a {@link Calendar} object as a Jackson - * {@link StdSerializer}. - *

- * This may be used to specify a deserializer for the new Java date API (e.g. {@link java.time.LocalDateTime}) based on - * a common conversion logic. This way the logic is split the following way: - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - *
General Conversion logicAbstractTypeConverter subclasses (*CalendarConverter)
Gson AdapterAbstractCalendarAdapter subclasses
Jackson (De)SerializerAbstractJacksonCalendar(De)Serializer and subclasses
- *

- * - * @param - * The type this serializer should read. - */ -public abstract class AbstractJacksonCalendarSerializer extends StdSerializer -{ - private static final long serialVersionUID = -7620182674695291969L; - - /** - * Constructor needed by the super class. - * - * @param t - * The class to be written by this serializer. - */ - protected AbstractJacksonCalendarSerializer( final Class t ) - { - super(t); - } - - /** - * Getter for an instance of the common conversion logic from and to {@code Calendar}. - * - * @return The conversion logic for this serializer. - */ - @Nonnull - protected abstract AbstractTypeConverter getConverterInstance(); - - /** - * Getter for the conversion of a Calendar object into a String. The selection of implementation depends on what - * kind of date object the string value should represent. - * - * @return {@code AbstractTypeConverter} instance that converts a Calendar into a String. - */ - @Nonnull - protected abstract AbstractTypeConverter getStringCalendarConverterInstance(); - - @Override - public void serialize( - @Nullable final T value, - @Nonnull final JsonGenerator jsonGenerator, - @Nonnull final SerializerProvider serializerProvider ) - throws IOException - { - final Calendar cal = getConverterInstance().toDomain(value).orNull(); - final ConvertedObject maybeJson = getStringCalendarConverterInstance().fromDomain(cal); - - if( maybeJson.isConvertible() ) { - new StringSerializer().serialize(maybeJson.get(), jsonGenerator, serializerProvider); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/IdentityConverter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/IdentityConverter.java deleted file mode 100644 index 3ea18c1139..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/IdentityConverter.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.typeconverter.ConvertedObject; -import com.sap.cloud.sdk.typeconverter.TypeConverter; - -/** - * Default implementation of the {@link TypeConverter} interface, returning all given objects unchanged. - */ -public class IdentityConverter implements TypeConverter -{ - @Override - @Nonnull - public ConvertedObject toDomain( @Nullable final Object object ) - { - return ConvertedObject.of(object); - } - - @Override - @Nonnull - public ConvertedObject fromDomain( @Nullable final Object domainObject ) - { - return ConvertedObject.of(domainObject); - } - - @Override - @Nonnull - public Class getType() - { - return Object.class; - } - - @Override - @Nonnull - public Class getDomainType() - { - return Object.class; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalDateTimeDeserializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalDateTimeDeserializer.java deleted file mode 100644 index cb8790412c..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalDateTimeDeserializer.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.LocalDateTime; -import java.util.Calendar; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; - -/** - * Jackson deserializer that is able to read {@link LocalDateTime} fields, based on a common logic reading from - * {@link Calendar}. - */ -public class JacksonLocalDateTimeDeserializer extends AbstractJacksonCalendarDeserializer -{ - private static final long serialVersionUID = 2970121420707666471L; - - /** - * Default constructor needed by the framework. - */ - public JacksonLocalDateTimeDeserializer() - { - super(LocalDateTime.class); - } - - @Nonnull - @Override - protected AbstractTypeConverter getCalendarConverterInstance() - { - return new LocalDateTimeCalendarConverter(); - } - - @Nonnull - @Override - protected AbstractTypeConverter getStringCalendarConverterInstance() - { - return new ODataDateTimeStringCalendarConverter(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalDateTimeSerializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalDateTimeSerializer.java deleted file mode 100644 index 56f8442d39..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalDateTimeSerializer.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.LocalDateTime; -import java.util.Calendar; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; - -/** - * Jackson deserializer that is able to write {@link LocalDateTime} fields, based on a common logic writing a - * {@link Calendar}. - */ -public class JacksonLocalDateTimeSerializer extends AbstractJacksonCalendarSerializer -{ - private static final long serialVersionUID = 17342648935256631L; - - /** - * Default constructor needed by the framework. - */ - protected JacksonLocalDateTimeSerializer() - { - super(LocalDateTime.class); - } - - @Override - @Nonnull - protected AbstractTypeConverter getConverterInstance() - { - return new LocalDateTimeCalendarConverter(); - } - - @Nonnull - @Override - protected AbstractTypeConverter getStringCalendarConverterInstance() - { - return new ODataDateTimeStringCalendarConverter(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalTimeDeserializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalTimeDeserializer.java deleted file mode 100644 index e604abc7ff..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalTimeDeserializer.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.LocalTime; -import java.util.Calendar; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; - -/** - * Jackson deserializer that is able to read {@link LocalTime} fields, based on a common logic reading from - * {@link Calendar}. - */ -public class JacksonLocalTimeDeserializer extends AbstractJacksonCalendarDeserializer -{ - private static final long serialVersionUID = -6461230022477505904L; - - /** - * Default constructor needed by the framework. - */ - protected JacksonLocalTimeDeserializer() - { - super(LocalTime.class); - } - - @Override - @Nonnull - protected AbstractTypeConverter getCalendarConverterInstance() - { - return new LocalTimeCalendarConverter(); - } - - @Nonnull - @Override - protected AbstractTypeConverter getStringCalendarConverterInstance() - { - return new ODataTimeStringCalendarConverter(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalTimeSerializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalTimeSerializer.java deleted file mode 100644 index 280df73f73..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonLocalTimeSerializer.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.LocalTime; -import java.util.Calendar; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; - -/** - * Jackson deserializer that is able to write {@link LocalTime} fields, based on a common logic writing a - * {@link Calendar}. - */ -public class JacksonLocalTimeSerializer extends AbstractJacksonCalendarSerializer -{ - private static final long serialVersionUID = 5786928813508456930L; - - /** - * Default constructor needed by the framework. - */ - protected JacksonLocalTimeSerializer() - { - super(LocalTime.class); - } - - @Override - @Nonnull - protected AbstractTypeConverter getConverterInstance() - { - return new LocalTimeCalendarConverter(); - } - - @Nonnull - @Override - protected AbstractTypeConverter getStringCalendarConverterInstance() - { - return new ODataTimeStringCalendarConverter(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonZonedDateTimeDeserializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonZonedDateTimeDeserializer.java deleted file mode 100644 index 37cfdb63a6..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonZonedDateTimeDeserializer.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.ZonedDateTime; -import java.util.Calendar; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; - -/** - * Jackson deserializer that is able to read {@link ZonedDateTime} fields, based on a common logic reading from - * {@link Calendar}. - */ -public class JacksonZonedDateTimeDeserializer extends AbstractJacksonCalendarDeserializer -{ - private static final long serialVersionUID = -7432353030570821745L; - - /** - * Default constructor needed by the framework. - */ - protected JacksonZonedDateTimeDeserializer() - { - super(ZonedDateTime.class); - } - - @Override - @Nonnull - protected AbstractTypeConverter getCalendarConverterInstance() - { - return new ZonedDateTimeCalendarConverter(); - } - - @Nonnull - @Override - protected AbstractTypeConverter getStringCalendarConverterInstance() - { - return new ODataDateTimeOffsetStringCalendarConverter(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonZonedDateTimeSerializer.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonZonedDateTimeSerializer.java deleted file mode 100644 index 24d8e61e06..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/JacksonZonedDateTimeSerializer.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.ZonedDateTime; -import java.util.Calendar; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; - -/** - * Jackson deserializer that is able to write {@link ZonedDateTime} fields, based on a common logic writing a - * {@link Calendar}. - */ -public class JacksonZonedDateTimeSerializer extends AbstractJacksonCalendarSerializer -{ - private static final long serialVersionUID = -3595139411304341322L; - - /** - * Default constructor needed by the framework. - */ - protected JacksonZonedDateTimeSerializer() - { - super(ZonedDateTime.class); - } - - @Override - @Nonnull - protected AbstractTypeConverter getConverterInstance() - { - return new ZonedDateTimeCalendarConverter(); - } - - @Nonnull - @Override - protected AbstractTypeConverter getStringCalendarConverterInstance() - { - return new ODataDateTimeOffsetStringCalendarConverter(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalDateTimeAdapter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalDateTimeAdapter.java deleted file mode 100644 index d9c251c347..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalDateTimeAdapter.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.LocalDateTime; -import java.util.Calendar; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -/** - * Gson adapter to (de-)serialize fields of type {@link LocalDateTime} from and to Json. - */ -public class LocalDateTimeAdapter extends AbstractCalendarAdapter -{ - private static final ODataDateTimeStringCalendarConverter STRING_CALENDAR_CONVERTER = - new ODataDateTimeStringCalendarConverter(); - private static final LocalDateTimeCalendarConverter LOCAL_DATE_TIME_CALENDAR_CONVERTER = - new LocalDateTimeCalendarConverter(); - - @Nonnull - @Override - protected ConvertedObject convertStringToType( @Nonnull final String jsonString ) - { - final ConvertedObject maybeCalendar = STRING_CALENDAR_CONVERTER.toDomain(jsonString); - return LOCAL_DATE_TIME_CALENDAR_CONVERTER.fromDomain(maybeCalendar.orNull()); - } - - @Nonnull - @Override - protected ConvertedObject convertTypeToString( @Nullable final LocalDateTime entity ) - { - final ConvertedObject convertedCalendar = LOCAL_DATE_TIME_CALENDAR_CONVERTER.toDomain(entity); - return STRING_CALENDAR_CONVERTER.fromDomain(convertedCalendar.orNull()); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalDateTimeCalendarConverter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalDateTimeCalendarConverter.java deleted file mode 100644 index ab43eb57d0..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalDateTimeCalendarConverter.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.LocalDateTime; -import java.time.temporal.ChronoField; -import java.util.Calendar; -import java.util.TimeZone; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -/** - * Converts between the deprecated {@link Calendar} type and the new {@link LocalDateTime}. - *

- * In combination with the {@link ODataField} annotation this can be used to expose fields of OData value which would be - * exposed as {@code Calendar} as {@code LocalDateTime}. - */ -public class LocalDateTimeCalendarConverter extends AbstractTypeConverter -{ - private static final int MILLI_TO_NANO_FACTOR = 1_000_000; - - @Override - @Nonnull - public ConvertedObject toDomainNonNull( @Nonnull final LocalDateTime object ) - { - // Used with the EdmDateTime class of the service SDK this calender has to be in timezone UTC, see - // Calendar.getTimeInMillis(), where the UTC usage is specified. - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - - calendar.set(Calendar.YEAR, object.get(ChronoField.YEAR)); - calendar.set(Calendar.MONTH, object.get(ChronoField.MONTH_OF_YEAR) - 1); // convert 1-based to 0-based months - calendar.set(Calendar.DAY_OF_MONTH, object.get(ChronoField.DAY_OF_MONTH)); - calendar.set(Calendar.HOUR_OF_DAY, object.get(ChronoField.HOUR_OF_DAY)); - calendar.set(Calendar.MINUTE, object.get(ChronoField.MINUTE_OF_HOUR)); - calendar.set(Calendar.SECOND, object.get(ChronoField.SECOND_OF_MINUTE)); - calendar.set(Calendar.MILLISECOND, object.get(ChronoField.MILLI_OF_SECOND)); - - return ConvertedObject.of(calendar); - } - - @Override - @Nonnull - public ConvertedObject fromDomainNonNull( @Nonnull final Calendar domainObject ) - { - final LocalDateTime localDateTime = - LocalDateTime - .of( - domainObject.get(Calendar.YEAR), - domainObject.get(Calendar.MONTH) + 1, // convert 0-based to 1-based months - domainObject.get(Calendar.DAY_OF_MONTH), - domainObject.get(Calendar.HOUR_OF_DAY), - domainObject.get(Calendar.MINUTE), - domainObject.get(Calendar.SECOND), - domainObject.get(Calendar.MILLISECOND) * MILLI_TO_NANO_FACTOR); - - return ConvertedObject.of(localDateTime); - } - - @Override - @Nonnull - public Class getType() - { - return LocalDateTime.class; - } - - @Override - @Nonnull - public Class getDomainType() - { - return Calendar.class; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalTimeAdapter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalTimeAdapter.java deleted file mode 100644 index 250349718a..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalTimeAdapter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.LocalTime; -import java.util.Calendar; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -/** - * Gson adapter to (de-)serialize fields of type {@link LocalTime} from and to Json. - */ -public class LocalTimeAdapter extends AbstractCalendarAdapter -{ - private static final ODataTimeStringCalendarConverter TIME_STRING_CALENDAR_CONVERTER = - new ODataTimeStringCalendarConverter(); - private static final LocalTimeCalendarConverter LOCAL_TIME_CALENDAR_CONVERTER = new LocalTimeCalendarConverter(); - - @Nonnull - @Override - protected ConvertedObject convertStringToType( @Nonnull final String jsonString ) - { - final ConvertedObject maybeCalendar = TIME_STRING_CALENDAR_CONVERTER.toDomain(jsonString); - return LOCAL_TIME_CALENDAR_CONVERTER.fromDomain(maybeCalendar.orNull()); - } - - @Nonnull - @Override - protected ConvertedObject convertTypeToString( @Nullable final LocalTime entity ) - { - final ConvertedObject convertedCalendar = LOCAL_TIME_CALENDAR_CONVERTER.toDomain(entity); - return TIME_STRING_CALENDAR_CONVERTER.fromDomain(convertedCalendar.orNull()); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalTimeCalendarConverter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalTimeCalendarConverter.java deleted file mode 100644 index 61f550e2d3..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalTimeCalendarConverter.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.LocalTime; -import java.time.temporal.ChronoField; -import java.util.Calendar; -import java.util.TimeZone; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -/** - * Converts between the deprecated {@link Calendar} type and the new {@link LocalTime}. - *

- * The year, month, and day fields on the {@code Calendar} instance are ignored/cleared, as the {@code LocalTime} does - * not contain any values for those - *

- * In combination with the {@link ODataField} annotation this can be used to expose fields of OData value which would be - * exposed as {@code Calendar} as {@code LocalTime}. - */ -public class LocalTimeCalendarConverter extends AbstractTypeConverter -{ - private static final int MILLI_TO_NANO_FACTOR = 1_000_000; - - @Override - @Nonnull - public ConvertedObject toDomainNonNull( @Nonnull final LocalTime object ) - { - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - - calendar.clear(); - calendar.set(Calendar.HOUR_OF_DAY, object.getHour()); - calendar.set(Calendar.MINUTE, object.getMinute()); - calendar.set(Calendar.SECOND, object.getSecond()); - if( object.isSupported(ChronoField.MILLI_OF_SECOND) ) { - calendar.set(Calendar.MILLISECOND, object.get(ChronoField.MILLI_OF_SECOND)); - } - - return ConvertedObject.of(calendar); - } - - @Override - @Nonnull - public ConvertedObject fromDomainNonNull( @Nonnull final Calendar domainObject ) - { - final LocalTime localDateTime = - LocalTime - .of( - domainObject.get(Calendar.HOUR_OF_DAY), - domainObject.get(Calendar.MINUTE), - domainObject.get(Calendar.SECOND), - domainObject.get(Calendar.MILLISECOND) * MILLI_TO_NANO_FACTOR); - - return ConvertedObject.of(localDateTime); - } - - @Override - @Nonnull - public Class getType() - { - return LocalTime.class; - } - - @Override - @Nonnull - public Class getDomainType() - { - return Calendar.class; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataBinaryAdapter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataBinaryAdapter.java deleted file mode 100644 index cf9706f7f7..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataBinaryAdapter.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -import lombok.extern.slf4j.Slf4j; - -/** - * For internal use only by data model classes - */ -@Slf4j -public class ODataBinaryAdapter extends TypeAdapter -{ - @Override - public void write( @Nonnull final JsonWriter jsonWriter, @Nullable final byte[] bytes ) - throws IOException - { - if( bytes == null ) { - jsonWriter.nullValue(); - return; - } - final String result = new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8); - jsonWriter.value(result); - } - - @Override - @Nullable - public byte[] read( @Nonnull final JsonReader jsonReader ) - throws IOException - { - if( jsonReader.peek() != JsonToken.STRING ) { - jsonReader.skipValue(); - return null; - } - - final String jsonValue = jsonReader.nextString(); - try { - return Base64.getDecoder().decode(jsonValue.getBytes(StandardCharsets.UTF_8)); - } - catch( final IllegalArgumentException e ) { - log.debug("Cannot decode String as byte array: " + e.getMessage()); - return null; - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataBooleanAdapter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataBooleanAdapter.java deleted file mode 100644 index c2420d8694..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataBooleanAdapter.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.io.IOException; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -/** - * For internal use only by data model classes - */ -public class ODataBooleanAdapter extends TypeAdapter -{ - /** - * For internal use only by data model classes - */ - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final Boolean entityValue ) - throws IOException - { - out.value(entityValue); - } - - /** - * For internal use only by data model classes - */ - @Override - @Nonnull - public Boolean read( @Nonnull final JsonReader in ) - throws IOException - { - if( in.peek() == JsonToken.BOOLEAN ) { - return in.nextBoolean(); - } else { - in.skipValue(); - return Boolean.FALSE; - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataCustomFieldAdapter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataCustomFieldAdapter.java deleted file mode 100644 index e985b6520e..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataCustomFieldAdapter.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; - -import lombok.RequiredArgsConstructor; - -/** - * For internal use only by data model classes - */ -@RequiredArgsConstructor -public class ODataCustomFieldAdapter extends TypeAdapter -{ - private final Gson gson; - - /** - * For internal use only by data model classes - */ - @Override - @Nullable - public Object read( @Nonnull final JsonReader in ) - throws IOException - { - switch( in.peek() ) { - case NUMBER: { - try { - return in.nextInt(); - } - catch( final NumberFormatException | IllegalStateException notAnInteger ) { - try { - return in.nextLong(); - } - catch( final NumberFormatException | IllegalStateException notALong ) { - return in.nextDouble(); - } - } - } - case BOOLEAN: { - return in.nextBoolean(); - } - case STRING: { - final String value = in.nextString(); - - if( !value.matches("/Date\\((-?\\p{Digit}+)\\)/") ) { - return value; - } - - return new ODataDateTimeStringCalendarConverter().toDomainNonNull(value).orNull(); - } - case BEGIN_ARRAY: { - in.beginArray(); - final List valueList = new ArrayList<>(); - - while( in.hasNext() ) { - valueList.add(read(in)); - } - - in.endArray(); - return valueList; - } - case BEGIN_OBJECT: { - in.beginObject(); - final Map valueObject = new HashMap<>(); - - while( in.hasNext() ) { - final String key = in.nextName(); - if( "__deferred".equals(key) ) { - in.skipValue(); - in.endObject(); - return null; - } else if( "__metadata".equals(key) ) { - in.skipValue(); - } else if( "results".equals(key) ) { - final Object valueList = read(in); - in.endObject(); - return valueList; - } else { - valueObject.put(key, read(in)); - } - } - - in.endObject(); - return valueObject; - } - default: { - in.skipValue(); - } - } - return null; - } - - /** - * For internal use only by data model classes - */ - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final Object value ) - throws IOException - { - // No need to do anything here. Serialization to JSON is handled generically by Gson. - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataDateTimeOffsetStringCalendarConverter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataDateTimeOffsetStringCalendarConverter.java deleted file mode 100644 index 96f5adeb27..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataDateTimeOffsetStringCalendarConverter.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -class ODataDateTimeOffsetStringCalendarConverter extends AbstractTypeConverter -{ - private static final Pattern JSON_PATTERN = - Pattern.compile("/Date\\((-?\\p{Digit}+)(?:(\\+|-)(\\p{Digit}{1,4}))?\\)/"); - - @Nonnull - @Override - public ConvertedObject toDomainNonNull( @Nonnull final String value ) - { - final Matcher jsonMatcher = JSON_PATTERN.matcher(value); - if( jsonMatcher.matches() ) { - long millis; - try { - millis = Long.parseLong(jsonMatcher.group(1)); - } - catch( final NumberFormatException e ) { - log.debug("The given date string cannot be converted to milliseconds: " + e.getMessage()); - return ConvertedObject.ofNotConvertible(); - } - - String timeZone = "GMT"; - if( jsonMatcher.group(2) != null ) { - final int offsetInMinutes = Integer.parseInt(jsonMatcher.group(3)); - if( offsetInMinutes >= 24 * 60 ) { - log.debug("The given offset is higher than minutes in a day: " + offsetInMinutes); - return ConvertedObject.ofNotConvertible(); - } - if( offsetInMinutes != 0 ) { - timeZone += - jsonMatcher.group(2) + offsetInMinutes / 60 + ":" + String.format("%02d", offsetInMinutes % 60); - // Convert the local-time milliseconds to UTC. - millis -= ("+".equals(jsonMatcher.group(2)) ? 1 : -1) * offsetInMinutes * 60 * 1000L; - } - } - final Calendar dateTimeValue = Calendar.getInstance(TimeZone.getTimeZone(timeZone)); - dateTimeValue.clear(); - dateTimeValue.setTimeInMillis(millis); - return ConvertedObject.of(dateTimeValue); - } - - return ConvertedObject.ofNotConvertible(); - } - - @Nonnull - @Override - public ConvertedObject fromDomainNonNull( @Nonnull final Calendar value ) - { - // number of milliseconds since 1970-01-01T00:00:00Z - long milliSeconds = value.getTimeInMillis(); - // offset in milliseconds from GMT to the requested time zone - final int offset = value.get(Calendar.ZONE_OFFSET) + value.get(Calendar.DST_OFFSET); - - milliSeconds += offset; // Convert from UTC to local time. - final int offsetInMinutes = offset / 60 / 1000; - - if( offset == 0 ) { - return ConvertedObject.of("/Date(" + milliSeconds + ")/"); - } - - return ConvertedObject.of("/Date(" + milliSeconds + String.format("%+05d", offsetInMinutes) + ")/"); - } - - @Nonnull - @Override - public Class getType() - { - return String.class; - } - - @Nonnull - @Override - public Class getDomainType() - { - return Calendar.class; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataDateTimeStringCalendarConverter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataDateTimeStringCalendarConverter.java deleted file mode 100644 index 3a0a8ce05d..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataDateTimeStringCalendarConverter.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -class ODataDateTimeStringCalendarConverter extends AbstractTypeConverter -{ - private static final Pattern JSON_PATTERN = Pattern.compile("/Date\\((-?\\p{Digit}+)\\)/"); - - @Nonnull - @Override - public ConvertedObject toDomainNonNull( @Nonnull final String value ) - { - final Matcher matcher = JSON_PATTERN.matcher(value); - if( !matcher.matches() ) { - return ConvertedObject.ofNotConvertible(); - } - - final long millis; - try { - millis = Long.parseLong(matcher.group(1)); - } - catch( final NumberFormatException e ) { - log.debug("The given date string cannot be converted to milliseconds: " + e.getMessage()); - return ConvertedObject.ofNotConvertible(); - } - final Calendar dateTimeValue = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - dateTimeValue.clear(); - dateTimeValue.setTimeInMillis(millis); - return ConvertedObject.of(dateTimeValue); - - } - - @Nonnull - @Override - public ConvertedObject fromDomainNonNull( @Nonnull final Calendar value ) - { - final long timeInMillis = value.getTimeInMillis(); - - return ConvertedObject.of("/Date(" + timeInMillis + ")/"); - } - - @Nonnull - @Override - public Class getType() - { - return String.class; - } - - @Nonnull - @Override - public Class getDomainType() - { - return Calendar.class; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataField.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataField.java deleted file mode 100644 index 7e02ac7a50..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataField.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.TypeConverter; - -/** - * Annotation to be used to link fields to their OData property as well as converting between the domain type of a field - * and the actually exposed type. - */ -@Target( ElementType.FIELD ) -@Retention( RetentionPolicy.RUNTIME ) -@Documented -public @interface ODataField { - /** - * The name of the OData property this field gets mapped to. - * - * @return The name of the corresponding OData property. - */ - @Nonnull - String odataName(); - - /** - * The converter to be used to convert between the domain and the exposed type of the annotated field. - * - * @return The type of the converter to use for the annotated field. - */ - @Nonnull - Class> converter() default IdentityConverter.class; -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataTimeStringCalendarConverter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataTimeStringCalendarConverter.java deleted file mode 100644 index 6c3f03bf10..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataTimeStringCalendarConverter.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.util.Calendar; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -class ODataTimeStringCalendarConverter extends AbstractTypeConverter -{ - private static final Pattern PATTERN = Pattern.compile(""" - P(?:(\\p{Digit}{1,2})Y)?(?:(\\p{Digit}{1,2})M)?(?:(\\p{Digit}{1,2})D)?\ - T(?:(\\p{Digit}{1,2})H)?(?:(\\p{Digit}{1,4})M)?(?:(\\p{Digit}{1,5})(?:\\.(\\p{Digit}+?)0*)?S)?\ - """); - - @Nonnull - @Override - public ConvertedObject toDomainNonNull( @Nonnull final String jsonValue ) - { - final Matcher matcher = PATTERN.matcher(jsonValue); - if( !matcher.matches() ) { - return ConvertedObject.ofNotConvertible(); - } - if( matcher.group(1) == null - && matcher.group(2) == null - && matcher.group(3) == null - && matcher.group(4) == null - && matcher.group(5) == null - && matcher.group(6) == null ) { - return ConvertedObject.ofNotConvertible(); - } - - final Calendar dateTimeValue = Calendar.getInstance(); - dateTimeValue.clear(); - - if( matcher.group(1) != null ) { - dateTimeValue.set(Calendar.YEAR, Integer.parseInt(matcher.group(1))); - } - if( matcher.group(2) != null ) { - dateTimeValue.set(Calendar.MONTH, Integer.parseInt(matcher.group(2))); - } - if( matcher.group(3) != null ) { - dateTimeValue.set(Calendar.DAY_OF_YEAR, Integer.parseInt(matcher.group(3))); - } - dateTimeValue.set(Calendar.HOUR_OF_DAY, matcher.group(4) == null ? 0 : Integer.parseInt(matcher.group(4))); - dateTimeValue.set(Calendar.MINUTE, matcher.group(5) == null ? 0 : Integer.parseInt(matcher.group(5))); - dateTimeValue.set(Calendar.SECOND, matcher.group(6) == null ? 0 : Integer.parseInt(matcher.group(6))); - - if( matcher.group(7) != null ) { - final String decimals = matcher.group(7); - final int nanoSeconds = Integer.parseInt(decimals + "000000000".substring(decimals.length())); - if( nanoSeconds % (1000 * 1000) == 0 ) { - dateTimeValue.set(Calendar.MILLISECOND, nanoSeconds / (1000 * 1000)); - } else { - log - .debug( - "The given date has a precision that cannot be represented in milliseconds. Nanoseconds: " - + nanoSeconds); - return ConvertedObject.ofNotConvertible(); - } - } - - return ConvertedObject.of(dateTimeValue); - } - - @Nonnull - @Override - public ConvertedObject fromDomainNonNull( @Nonnull final Calendar calendar ) - { - final StringBuilder result = new StringBuilder(21); // 21 characters are enough for nanosecond precision. - result.append('P'); - result.append('T'); - result.append(calendar.get(Calendar.HOUR_OF_DAY)); - result.append('H'); - result.append(calendar.get(Calendar.MINUTE)); - result.append('M'); - result.append(calendar.get(Calendar.SECOND)); - - final int fractionalSecs = calendar.get(Calendar.MILLISECOND); - appendFractionalSeconds(result, fractionalSecs); - result.append('S'); - - return ConvertedObject.of(result.toString()); - } - - protected static void appendFractionalSeconds( final StringBuilder result, final int fractionalSeconds ) - { - if( fractionalSeconds > 0 ) { - // Determine the number of significant digits. - int output = fractionalSeconds; - while( output % 10 == 0 ) { - output /= 10; - } - - result.append('.'); - for( int d = 100; d > 0; d /= 10 ) { - final byte digit = (byte) (fractionalSeconds % (d * 10) / d); - if( digit > 0 || fractionalSeconds % d > 0 ) { - result.append((char) ('0' + digit)); - } - } - } - } - - @Nonnull - @Override - public Class getType() - { - return String.class; - } - - @Nonnull - @Override - public Class getDomainType() - { - return Calendar.class; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataVdmEntityAdapter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataVdmEntityAdapter.java deleted file mode 100644 index 0426b5e285..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataVdmEntityAdapter.java +++ /dev/null @@ -1,301 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import static java.util.function.Predicate.not; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.IdentityHashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.common.base.Strings; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import com.sap.cloud.sdk.datamodel.odata.helper.VdmEntity; -import com.sap.cloud.sdk.datamodel.odata.helper.VdmObject; -import com.sap.cloud.sdk.result.ElementName; - -import io.vavr.control.Option; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -/** - * For internal use only by data model classes - * - * @param - * The generic VDM object type - */ -@Slf4j -public class ODataVdmEntityAdapter extends TypeAdapter> -{ - @Nonnull - private final Gson gson; - - @Nonnull - private final Class entityRawType; - - @Nullable - private TypeAdapter> delegateAdapter = null; - - @Nullable - private ODataVdmEntityAdapter superClassAdapter = null; - - @Nonnull - private final TypeAdapter customFieldAdapter; - - // (1) Field has stateful accessibility flag - // (2) Multiple threads may access the Map, therefore state must be isolated with ThreadLocal - // (3) ThreadLocal is to be defined statically, therefore the properties must be mapped to a class reference - private static final ThreadLocal, Map>> fieldProperties = - ThreadLocal.withInitial(IdentityHashMap::new); - - @Nonnull - private Map getFieldProperties( @Nonnull final Class type ) - { - return fieldProperties.get().computeIfAbsent(type, ODataVdmEntityAdapter::createFieldProperties); - } - - @Nonnull - private static Map createFieldProperties( @Nonnull final Class type ) - { - final Map result = new LinkedHashMap<>(); - for( final Field field : type.getDeclaredFields() ) { - if( field.isAnnotationPresent(ElementName.class) || field.isAnnotationPresent(SerializedName.class) ) { - final String odataName = - field.isAnnotationPresent(ElementName.class) - ? field.getAnnotation(ElementName.class).value() - : field.getAnnotation(SerializedName.class).value(); - result.put(odataName, field); - } - } - return result; - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - private static class ODataV2Metadata - { - @Nullable - @SerializedName( "uri" ) - @ODataField( odataName = "uri" ) - private String uri; - - @Nullable - @SerializedName( "etag" ) - @ODataField( odataName = "etag" ) - private String etag; - - @Nullable - @SerializedName( "type" ) - @ODataField( odataName = "type" ) - private String type; - } - - @Nonnull - private static TypeAdapter getAdapterFromField( @Nonnull final Field entityField, @Nonnull final Gson gson ) - { - if( entityField.isAnnotationPresent(JsonAdapter.class) ) { - try { - return (TypeAdapter) entityField - .getAnnotation(JsonAdapter.class) - .value() - .getDeclaredConstructor() - .newInstance(); - } - catch( final - InstantiationException - | IllegalAccessException - | NoSuchMethodException - | InvocationTargetException e ) { - log.warn("Could not instantiate the field '" + entityField.getName() + "'.", e); - } - } - if( Iterable.class.isAssignableFrom(entityField.getType()) ) { - final ParameterizedType entityFieldTypeParams = (ParameterizedType) entityField.getGenericType(); - final Type listEntityType = entityFieldTypeParams.getActualTypeArguments()[0]; - final TypeAdapter innerTypeAdapter = gson.getAdapter(TypeToken.get(listEntityType)); - return new ODataVdmEntityListAdapter<>(gson, innerTypeAdapter); - } - return gson.getAdapter(entityField.getType()); - } - - /** - * For internal use only by data model classes - * - * @param adapterFactory - * The GSON type adapter factory - * @param gson - * The GSON reference - * @param entityRawType - * The generic entity raw type - */ - @SuppressWarnings( "unchecked" ) - public ODataVdmEntityAdapter( - @Nonnull final TypeAdapterFactory adapterFactory, - @Nonnull final Gson gson, - @Nonnull final Class entityRawType ) - { - this.gson = gson; - this.entityRawType = entityRawType; - customFieldAdapter = new ODataCustomFieldAdapter(gson); - - final Class entityRawSuperType = entityRawType.getSuperclass(); - if( Object.class == entityRawSuperType ) { - delegateAdapter = - (TypeAdapter>) gson.getDelegateAdapter(adapterFactory, TypeToken.get(entityRawType)); - } else { - superClassAdapter = new ODataVdmEntityAdapter<>(adapterFactory, gson, entityRawSuperType); - } - } - - /** - * For internal use only by data model classes - */ - @Override - @Nullable - public VdmObject read( @Nonnull final JsonReader jsonReader ) - throws IOException - { - try { - @SuppressWarnings( "unchecked" ) - final VdmObject entity = (VdmObject) entityRawType.getDeclaredConstructor().newInstance(); - - if( jsonReader.peek() == JsonToken.BEGIN_OBJECT ) { - jsonReader.beginObject(); - - while( jsonReader.hasNext() ) { - final String propertyKey = jsonReader.nextName(); - - if( "__metadata".equals(propertyKey) ) { - if( jsonReader.peek() == JsonToken.BEGIN_OBJECT ) { - final ODataV2Metadata metadata = new Gson().fromJson(jsonReader, ODataV2Metadata.class); - final Option maybeEtag = - Option.of(metadata).map(ODataV2Metadata::getEtag).filter(not(Strings::isNullOrEmpty)); - if( maybeEtag.isDefined() && entity instanceof VdmEntity ) { - ((VdmEntity) entity).setVersionIdentifier(maybeEtag.get()); - } - } else { - log.warn("Expected JSON value \"__metadata\" to be an object."); - jsonReader.skipValue(); - } - } else if( "__deferred".equals(propertyKey) ) { - jsonReader.skipValue(); - jsonReader.endObject(); - return null; - } else { - final Field entityField = getPropertySerializationInfo(propertyKey); - if( entityField != null ) { - final TypeAdapter fieldAdapter = getAdapterFromField(entityField, gson); - final Object attributeValue = fieldAdapter.read(jsonReader); - - // To be safe/secure, since fields are declared private in the VDM. - final boolean oldAccessibleValue = entityField.canAccess(entity); - entityField.setAccessible(true); - entityField.set(entity, attributeValue); - entityField.setAccessible(oldAccessibleValue); - } else { - entity.getCustomFields().put(propertyKey, customFieldAdapter.read(jsonReader)); - } - } - } - - jsonReader.endObject(); - } else if( jsonReader.peek() == JsonToken.NULL ) { - jsonReader.nextNull(); - return null; - } - - return entity; - } - catch( final - InstantiationException - | IllegalAccessException - | NoSuchMethodException - | InvocationTargetException e ) { - log.error("Could not instantiate or initialize '{}'. Returning null instead.", entityRawType.getName(), e); - } - return null; - } - - @Nullable - private Field getPropertySerializationInfo( final String propertyKey ) - { - Field result = getFieldProperties(entityRawType).get(propertyKey); - if( result == null && superClassAdapter != null ) { - result = superClassAdapter.getPropertySerializationInfo(propertyKey); - } - return result; - } - - /** - * For internal use only by data model classes - */ - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final VdmObject value ) - throws IOException - { - if( value != null ) { - final JsonObject entityAsJson = getEntityAsJsonObject(value); - final JsonObject customFieldsAsJson = gson.toJsonTree(value.getCustomFields()).getAsJsonObject(); - - for( final Map.Entry customField : customFieldsAsJson.entrySet() ) { - entityAsJson.add(customField.getKey(), customField.getValue()); - } - - gson.toJson(entityAsJson, out); - } else { - out.nullValue(); - } - } - - @SuppressWarnings( "unchecked" ) - private JsonObject getEntityAsJsonObject( final VdmObject value ) - { - if( delegateAdapter != null ) { - return delegateAdapter.toJsonTree(value).getAsJsonObject(); - } else { - final JsonObject entityAsJson = superClassAdapter.getEntityAsJsonObject(value); - - for( final Map.Entry entityProperty : getFieldProperties(entityRawType).entrySet() ) { - try { - final Field propertyField = entityProperty.getValue(); - - // To be safe/secure, since fields are declared private in the VDM. - final boolean oldAccessibleValue = propertyField.canAccess(value); - propertyField.setAccessible(true); - final Object propertyValue = propertyField.get(value); - propertyField.setAccessible(oldAccessibleValue); - - final TypeAdapter fieldAdapter = - (TypeAdapter) getAdapterFromField(propertyField, gson); - final JsonElement propertyValueAsJson = fieldAdapter.toJsonTree(propertyValue); - - // Overwrites JSON property from the superclass if this class has a property with the same name. - entityAsJson.add(entityProperty.getKey(), propertyValueAsJson); - } - catch( final IllegalAccessException e ) { - log.error("Could not serialize property '{}'. Returning null instead.", entityProperty.getKey(), e); - } - } - return entityAsJson; - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataVdmEntityAdapterFactory.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataVdmEntityAdapterFactory.java deleted file mode 100644 index f563087624..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataVdmEntityAdapterFactory.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.sap.cloud.sdk.datamodel.odata.helper.VdmObject; - -/** - * For internal use only by data model classes. - */ -@SuppressWarnings( "unchecked" ) -public class ODataVdmEntityAdapterFactory implements TypeAdapterFactory -{ - /** - * For internal use only by data model classes. - * - * {@inheritDoc} - */ - @Override - @Nullable - public TypeAdapter create( @Nonnull final Gson gson, @Nonnull final TypeToken type ) - { - final Class entityType = type.getRawType(); - - if( VdmObject.class.isAssignableFrom(entityType) ) { - return (TypeAdapter) new ODataVdmEntityAdapter<>(this, gson, entityType); - } - return null; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataVdmEntityListAdapter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataVdmEntityListAdapter.java deleted file mode 100644 index 4093f95aec..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataVdmEntityListAdapter.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -/** - * For internal use only by data model classes - * - * @param - * The generic value type. - */ -public class ODataVdmEntityListAdapter extends TypeAdapter> -{ - private final Gson gson; - private final TypeAdapter entityAdapter; - - /** - * For internal use only by data model classes - * - * @param gson - * The GSON instance to access serialization and desrialization. - * @param entityAdapter - * The entity type adapter to be used. - */ - public ODataVdmEntityListAdapter( @Nonnull final Gson gson, @Nonnull final TypeAdapter entityAdapter ) - { - this.gson = gson; - this.entityAdapter = entityAdapter; - } - - private List readArray( final JsonReader in ) - throws IOException - { - in.beginArray(); - final List entityList = new ArrayList<>(); - - while( in.hasNext() ) { - @SuppressWarnings( "unchecked" ) - final T entity = entityAdapter.read(in); - entityList.add(entity); - } - - in.endArray(); - return entityList; - } - - /** - * For internal use only by data model classes - */ - @Override - @Nullable - public List read( @Nonnull final JsonReader in ) - throws IOException - { - List entityList = null; - if( in.peek() == JsonToken.BEGIN_OBJECT ) { - in.beginObject(); - if( in.peek() == JsonToken.NAME ) { - final String resultsKey = in.nextName(); - if( "results".equals(resultsKey) && in.peek() == JsonToken.BEGIN_ARRAY ) { - entityList = readArray(in); - } else { - in.skipValue(); - } - } - in.endObject(); - } else if( in.peek() == JsonToken.BEGIN_ARRAY ) { - entityList = readArray(in); - } else { - in.skipValue(); - } - return entityList; - } - - /** - * For internal use only by data model classes - */ - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final List entityList ) - throws IOException - { - if( entityList != null ) { - final JsonArray entityListAsJson = new JsonArray(); - for( final T entity : entityList ) { - entityListAsJson.add(entityAdapter.toJsonTree(entity)); - } - gson.toJson(entityListAsJson, out); - } else { - out.nullValue(); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ZonedDateTimeAdapter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ZonedDateTimeAdapter.java deleted file mode 100644 index 1a2bf9eaf4..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ZonedDateTimeAdapter.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.ZonedDateTime; -import java.util.Calendar; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -/** - * Gson adapter to (de-)serialize fields of type {@link ZonedDateTime} from and to Json. - */ -public class ZonedDateTimeAdapter extends AbstractCalendarAdapter -{ - private static final ODataDateTimeOffsetStringCalendarConverter DATE_TIME_OFFSET_STRING_CALENDAR_CONVERTER = - new ODataDateTimeOffsetStringCalendarConverter(); - private static final ZonedDateTimeCalendarConverter ZONED_DATE_TIME_CALENDAR_CONVERTER = - new ZonedDateTimeCalendarConverter(); - - @Nonnull - @Override - protected ConvertedObject convertStringToType( @Nonnull final String jsonString ) - { - final ConvertedObject maybeCalendar = DATE_TIME_OFFSET_STRING_CALENDAR_CONVERTER.toDomain(jsonString); - return ZONED_DATE_TIME_CALENDAR_CONVERTER.fromDomain(maybeCalendar.orNull()); - } - - @Nonnull - @Override - protected ConvertedObject convertTypeToString( @Nullable final ZonedDateTime entity ) - { - final ConvertedObject convertedCalendar = ZONED_DATE_TIME_CALENDAR_CONVERTER.toDomain(entity); - return DATE_TIME_OFFSET_STRING_CALENDAR_CONVERTER.fromDomain(convertedCalendar.orNull()); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ZonedDateTimeCalendarConverter.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ZonedDateTimeCalendarConverter.java deleted file mode 100644 index a9d0e2c15c..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ZonedDateTimeCalendarConverter.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import java.time.ZonedDateTime; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -/** - * Converts between the deprecated {@link Calendar} type and the new {@link ZonedDateTime}. - *

- * In combination with the {@link ODataField} annotation this can be used to expose fields of OData value which would be - * exposed as {@code Calendar} as {@code ZonedDateTime}. - */ -public class ZonedDateTimeCalendarConverter extends AbstractTypeConverter -{ - @Override - @Nonnull - public ConvertedObject toDomainNonNull( @Nonnull final ZonedDateTime object ) - { - final Calendar cal = GregorianCalendar.from(object); - return ConvertedObject.of(cal); - } - - @Override - @Nonnull - public ConvertedObject fromDomainNonNull( @Nonnull final Calendar domainObject ) - { - final TimeZone timeZone = domainObject.getTimeZone(); - final ZonedDateTime zdt = ZonedDateTime.ofInstant(domainObject.toInstant(), timeZone.toZoneId()); - return ConvertedObject.of(zdt); - } - - @Override - @Nonnull - public Class getType() - { - return ZonedDateTime.class; - } - - @Override - @Nonnull - public Class getDomainType() - { - return Calendar.class; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/annotation/Key.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/annotation/Key.java deleted file mode 100644 index 7ee198fa07..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/annotation/Key.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to mark a field in a VDM entity as being a key field. This means that the annotated field plus any other - * fields with this annotation uniquely identify an instance of - * {@link com.sap.cloud.sdk.datamodel.odata.helper.VdmEntity}. The VDM generator will add this annotation to the entity - * classes it creates based on the OData EDMX ({@code } tag under {@code }) - */ -@Retention( RetentionPolicy.RUNTIME ) -@Target( ElementType.FIELD ) -public @interface Key { -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/exception/NoSuchEntityFieldException.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/exception/NoSuchEntityFieldException.java deleted file mode 100644 index e865b80a7b..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/exception/NoSuchEntityFieldException.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.exception; - -import javax.annotation.Nullable; - -import lombok.NoArgsConstructor; - -/** - * Throws if a certain field cannot be found for an entity. - */ -@NoArgsConstructor -public class NoSuchEntityFieldException extends RuntimeException -{ - private static final long serialVersionUID = -5897105662911702521L; - - /** - * Initializes a new {@link NoSuchEntityFieldException} instance. - * - * @param message - * The exception message. - */ - public NoSuchEntityFieldException( @Nullable final String message ) - { - super(message); - } - - /** - * Initializes a new {@link NoSuchEntityFieldException} instance. - * - * @param cause - * The exception cause. - */ - public NoSuchEntityFieldException( @Nullable final Throwable cause ) - { - super(cause); - } - - /** - * Initializes a new {@link NoSuchEntityFieldException} instance. - * - * @param message - * The exception message. - * @param cause - * The exception cause. - */ - public NoSuchEntityFieldException( @Nullable final String message, @Nullable final Throwable cause ) - { - super(message, cause); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/exception/ODataPayloadParsingFailedException.java b/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/exception/ODataPayloadParsingFailedException.java deleted file mode 100644 index ddc84b08d0..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/s4hana/datamodel/odata/exception/ODataPayloadParsingFailedException.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.exception; - -import javax.annotation.Nullable; - -/** - * ODataPayloadParsingFailedException will be thrown whenever the VDM encounters errors during parsing that cannot be - * recovered from. Possible causes are when the ODataJsonMapResolver is instructed to throw exceptions when encountering - * null values or when TypeConverters for specific fields cannot be instantiated. - */ -public class ODataPayloadParsingFailedException extends RuntimeException -{ - private static final long serialVersionUID = 6446357797006978261L; - - /** - * Returns a new ODataPayloadParsingFailedException instance. - * - * @param message - * The error message. - * @param cause - * The exception causing the error. - */ - public ODataPayloadParsingFailedException( @Nullable final String message, @Nullable final Exception cause ) - { - super(message, cause); - } - - /** - * Returns a new ODataPayloadParsingFailedException instance. - * - * @param message - * The error message. - */ - public ODataPayloadParsingFailedException( @Nullable final String message ) - { - super(message); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/LocalDateTimeCalendarConverterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/LocalDateTimeCalendarConverterTest.java deleted file mode 100644 index d0395546dd..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/LocalDateTimeCalendarConverterTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDateTime; -import java.time.Month; -import java.time.temporal.ChronoField; -import java.util.Calendar; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalDateTimeCalendarConverter; - -class LocalDateTimeCalendarConverterTest -{ - @Test - void testFromDomain() - { - final int someYear = 2017; - final int someMonth = Calendar.MAY; - final int someDay = 24; - final int someHour = 15; - final int someMinute = 57; - final int someSecond = 42; - final int someMillisecond = 852; - - final Calendar input = Calendar.getInstance(); - input.clear(); - input.set(Calendar.YEAR, someYear); - input.set(Calendar.MONTH, someMonth); - input.set(Calendar.DAY_OF_MONTH, someDay); - input.set(Calendar.HOUR_OF_DAY, someHour); - input.set(Calendar.MINUTE, someMinute); - input.set(Calendar.SECOND, someSecond); - input.set(Calendar.MILLISECOND, someMillisecond); - - final LocalDateTime result = new LocalDateTimeCalendarConverter().fromDomain(input).get(); - - assertThat(result).isNotNull(); - - assertThat(result.get(ChronoField.YEAR)).isEqualTo(someYear); - assertThat(result.get(ChronoField.MONTH_OF_YEAR)).isEqualTo(Month.MAY.getValue()); - assertThat(result.get(ChronoField.DAY_OF_MONTH)).isEqualTo(someDay); - assertThat(result.get(ChronoField.HOUR_OF_DAY)).isEqualTo(someHour); - assertThat(result.get(ChronoField.MINUTE_OF_HOUR)).isEqualTo(someMinute); - assertThat(result.get(ChronoField.SECOND_OF_MINUTE)).isEqualTo(someSecond); - assertThat(result.get(ChronoField.MILLI_OF_SECOND)).isEqualTo(someMillisecond); - } - - @Test - void testToDomain() - { - final int someYear = 2017; - final int someMonth = Month.MAY.getValue(); - final int someDay = 24; - final int someHour = 15; - final int someMinute = 57; - final int someSecond = 42; - final int someMillisecond = 852; - final int millisAsNanoseconds = someMillisecond * 1_000_000; - - final LocalDateTime input = - LocalDateTime.of(someYear, someMonth, someDay, someHour, someMinute, someSecond, millisAsNanoseconds); - - final Calendar result = new LocalDateTimeCalendarConverter().toDomain(input).get(); - - assertThat(result).isNotNull(); - - assertThat(result.get(Calendar.YEAR)).isEqualTo(someYear); - assertThat(result.get(Calendar.MONTH)).isEqualTo(Calendar.MAY); - assertThat(result.get(Calendar.DAY_OF_MONTH)).isEqualTo(someDay); - assertThat(result.get(Calendar.HOUR_OF_DAY)).isEqualTo(someHour); - assertThat(result.get(Calendar.MINUTE)).isEqualTo(someMinute); - assertThat(result.get(Calendar.SECOND)).isEqualTo(someSecond); - assertThat(result.get(Calendar.MILLISECOND)).isEqualTo(someMillisecond); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/LocalTimeCalendarConverterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/LocalTimeCalendarConverterTest.java deleted file mode 100644 index 11062fa961..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/LocalTimeCalendarConverterTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalTime; -import java.time.temporal.ChronoField; -import java.util.Calendar; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalTimeCalendarConverter; - -class LocalTimeCalendarConverterTest -{ - @Test - void testFromDomain() - { - final int someHour = 15; - final int someMinute = 57; - final int someSecond = 42; - final int someMillisecond = 852; - - final Calendar input = Calendar.getInstance(); - input.clear(); - input.set(Calendar.HOUR_OF_DAY, someHour); - input.set(Calendar.MINUTE, someMinute); - input.set(Calendar.SECOND, someSecond); - input.set(Calendar.MILLISECOND, someMillisecond); - - final LocalTime result = new LocalTimeCalendarConverter().fromDomain(input).get(); - - assertThat(result).isNotNull(); - - assertThat(result.get(ChronoField.HOUR_OF_DAY)).isEqualTo(someHour); - assertThat(result.get(ChronoField.MINUTE_OF_HOUR)).isEqualTo(someMinute); - assertThat(result.get(ChronoField.SECOND_OF_MINUTE)).isEqualTo(someSecond); - assertThat(result.get(ChronoField.MILLI_OF_SECOND)).isEqualTo(someMillisecond); - } - - @Test - void testToDomain() - { - final int someHour = 15; - final int someMinute = 57; - final int someSecond = 42; - final int someMillisecond = 852; - final int millisAsNanoseconds = someMillisecond * 1_000_000; - - final LocalTime input = LocalTime.of(someHour, someMinute, someSecond, millisAsNanoseconds); - - final Calendar result = new LocalTimeCalendarConverter().toDomain(input).get(); - - assertThat(result).isNotNull(); - - assertThat(result.get(Calendar.HOUR_OF_DAY)).isEqualTo(someHour); - assertThat(result.get(Calendar.MINUTE)).isEqualTo(someMinute); - assertThat(result.get(Calendar.SECOND)).isEqualTo(someSecond); - assertThat(result.get(Calendar.MILLISECOND)).isEqualTo(someMillisecond); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/ZonedDateTimeAdapterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/ZonedDateTimeAdapterTest.java deleted file mode 100644 index 4572fd5764..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/ZonedDateTimeAdapterTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.io.StringReader; -import java.time.ZoneId; -import java.time.ZonedDateTime; - -import org.junit.jupiter.api.Test; - -import com.google.gson.Strictness; -import com.google.gson.stream.JsonReader; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ZonedDateTimeAdapter; - -class ZonedDateTimeAdapterTest -{ - - @Test - void testDeserialization() - throws IOException - { - final String jsonInput = "\"/Date(1525730400000-0120)/\""; - - final ZonedDateTimeAdapter sut = new ZonedDateTimeAdapter(); - final JsonReader reader = new JsonReader(new StringReader(jsonInput)); - reader.setStrictness(Strictness.LENIENT); - final ZonedDateTime result = sut.read(reader); - - assertThat(result).isEqualTo(ZonedDateTime.of(2018, 5, 8, 2, 0, 0, 0, ZoneId.of("UTC+2"))); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/ZonedDateTimeCalendarConverterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/ZonedDateTimeCalendarConverterTest.java deleted file mode 100644 index a8a7b118e6..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/adapter/ZonedDateTimeCalendarConverterTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Month; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoField; -import java.util.Calendar; -import java.util.TimeZone; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ZonedDateTimeCalendarConverter; - -class ZonedDateTimeCalendarConverterTest -{ - @Test - void testFromDomain() - { - final int someYear = 2017; - final int someMonth = Calendar.MAY; - final int someDay = 24; - final int someHour = 15; - final int someMinute = 57; - final int someSecond = 42; - final int someMillisecond = 852; - final TimeZone someTimeZone = TimeZone.getTimeZone("America/Los_Angeles"); - - final Calendar input = Calendar.getInstance(someTimeZone); - input.clear(); - input.set(Calendar.YEAR, someYear); - input.set(Calendar.MONTH, someMonth); - input.set(Calendar.DAY_OF_MONTH, someDay); - input.set(Calendar.HOUR_OF_DAY, someHour); - input.set(Calendar.MINUTE, someMinute); - input.set(Calendar.SECOND, someSecond); - input.set(Calendar.MILLISECOND, someMillisecond); - - final ZonedDateTime result = new ZonedDateTimeCalendarConverter().fromDomain(input).get(); - - assertThat(result).isNotNull(); - - assertThat(result.getZone()).isEqualTo(someTimeZone.toZoneId()); - assertThat(result.get(ChronoField.YEAR)).isEqualTo(someYear); - assertThat(result.get(ChronoField.MONTH_OF_YEAR)).isEqualTo(Month.MAY.getValue()); - assertThat(result.get(ChronoField.DAY_OF_MONTH)).isEqualTo(someDay); - assertThat(result.get(ChronoField.HOUR_OF_DAY)).isEqualTo(someHour); - assertThat(result.get(ChronoField.MINUTE_OF_HOUR)).isEqualTo(someMinute); - assertThat(result.get(ChronoField.SECOND_OF_MINUTE)).isEqualTo(someSecond); - assertThat(result.get(ChronoField.MILLI_OF_SECOND)).isEqualTo(someMillisecond); - } - - @Test - void testToDomain() - { - final int someYear = 2017; - final int someMonth = Month.MAY.getValue(); - final int someDay = 24; - final int someHour = 15; - final int someMinute = 57; - final int someSecond = 42; - final int someMillisecond = 852; - final int millisAsNanoseconds = someMillisecond * 1_000_000; - final ZoneId someTimeZone = ZoneId.of("America/Los_Angeles"); - - final ZonedDateTime input = - ZonedDateTime - .of(someYear, someMonth, someDay, someHour, someMinute, someSecond, millisAsNanoseconds, someTimeZone); - - final Calendar result = new ZonedDateTimeCalendarConverter().toDomain(input).get(); - - assertThat(result).isNotNull(); - - assertThat(result.getTimeZone()).isEqualTo(TimeZone.getTimeZone(someTimeZone)); - assertThat(result.get(Calendar.YEAR)).isEqualTo(someYear); - assertThat(result.get(Calendar.MONTH)).isEqualTo(Calendar.MAY); - assertThat(result.get(Calendar.DAY_OF_MONTH)).isEqualTo(someDay); - assertThat(result.get(Calendar.HOUR_OF_DAY)).isEqualTo(someHour); - assertThat(result.get(Calendar.MINUTE)).isEqualTo(someMinute); - assertThat(result.get(Calendar.SECOND)).isEqualTo(someSecond); - assertThat(result.get(Calendar.MILLISECOND)).isEqualTo(someMillisecond); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/CreateViaNavigationPropertyTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/CreateViaNavigationPropertyTest.java deleted file mode 100644 index a1ffb17005..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/CreateViaNavigationPropertyTest.java +++ /dev/null @@ -1,236 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.created; -import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.core5.http.HttpHeaders; -import org.assertj.core.api.InstanceOfAssertFactories; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataServiceErrorDetails; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataServiceErrorException; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.With; - -@WireMockTest -class CreateViaNavigationPropertyTest -{ - private static final String ODATA_SERVICE_PATH = "/service/path"; - private static final String ODATA_ENTITY = "A_TestEntity"; - private static final String ODATA_ENTITY_RELATED = "A_RelatedEntity"; - private static final String KEY = "('2')"; - private static final String RELATIONS_NAV_PROP = "to_Relations"; - - private static final String JSON_MOCK_REQUEST = "{\"Description\" :\"DE\"}"; - private static final String JSON_CREATED_RESPONSE = """ - { - "d": { - "__metadata": { - "id": "https://127.0.0.1/service/path/A_TestEntity(Name='2',Relation='652138')", - "uri": "https://127.0.0.1/service/path/A_TestEntity(Name='2',Relation='652138')", - "type": "TEST_SERVICE.A_RelatedEntityType" - } - } - } - """; - private static final String JSON_NOT_IMPLEMENTED_RESPONSE = """ - { - "error": { - "code": "AB/100", - "message": { - "lang": "en", - "value": "Invalid method invocation: 'CREATE' method is called on the non-root entity 'A_RelatedEntity'" - }, - "innererror": { - "application": { - "component_id": "AB-CDE-FGH-IJ", - "service_namespace": "/SAP/", - "service_id": "TEST_SERVICE", - "service_version": "0001" - }, - "transactionid": "10000", - "timestamp": "20180815132618.3364870", - "errordetails": [ - { - "code": "INNER-CODE-1", - "message": "INNER-MESSAGE-1", - "propertyref": "", - "severity": "error", - "target": "" - } - ] - } - } - } - """; - - private DefaultHttpDestination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - stubFor(head(urlEqualTo(ODATA_SERVICE_PATH + "/")).willReturn(ok())); - } - - @Test - void happyPathWorksAsExpected() - { - stubFor( - post(urlEqualTo(ODATA_SERVICE_PATH + "/" + ODATA_ENTITY + KEY + "/" + RELATIONS_NAV_PROP)) - .withRequestBody(equalToJson(JSON_MOCK_REQUEST)) - .willReturn(created().withBody(JSON_CREATED_RESPONSE))); - - FluentHelperFactory - .withServicePath(ODATA_SERVICE_PATH) - .create(ODATA_ENTITY, new RelatedEntity("DE")) - .asChildOf(new TestEntity().withName("2"), TestEntity.TO_RELATIONS) - .executeRequest(destination); - - verify(1, headRequestedFor(urlEqualTo(ODATA_SERVICE_PATH + "/"))); - verify( - 1, - postRequestedFor(urlEqualTo(ODATA_SERVICE_PATH + "/" + ODATA_ENTITY + KEY + "/" + RELATIONS_NAV_PROP))); - } - - @Test - void nullParametersResultInDefaultBehavior() - { - stubFor( - post(urlEqualTo(ODATA_SERVICE_PATH + "/" + ODATA_ENTITY_RELATED)) - .withRequestBody(equalToJson(JSON_MOCK_REQUEST)) - .willReturn(created().withBody(JSON_CREATED_RESPONSE))); - - FluentHelperFactory - .withServicePath(ODATA_SERVICE_PATH) - .create(ODATA_ENTITY_RELATED, new RelatedEntity("DE")) - .asChildOf(null, null) - .executeRequest(destination); - - verify(1, headRequestedFor(urlEqualTo(ODATA_SERVICE_PATH + "/"))); - verify(1, postRequestedFor(urlEqualTo(ODATA_SERVICE_PATH + "/" + ODATA_ENTITY_RELATED))); - } - - @Test - void directCreationFails() - { - stubFor( - post(urlEqualTo(ODATA_SERVICE_PATH + "/" + ODATA_ENTITY_RELATED)) - .withRequestBody(equalToJson(JSON_MOCK_REQUEST)) - .willReturn( - aResponse() - .withStatus(501) - .withHeader(HttpHeaders.CONTENT_TYPE, "application/json") - .withBody(JSON_NOT_IMPLEMENTED_RESPONSE))); - - assertThatCode( - () -> FluentHelperFactory - .withServicePath(ODATA_SERVICE_PATH) - .create(ODATA_ENTITY_RELATED, new RelatedEntity("DE")) - .executeRequest(destination)) - .hasMessage( - "The HTTP response code (501) indicates an error. The OData service responded with an error message.") - .asInstanceOf(InstanceOfAssertFactories.type(ODataServiceErrorException.class)) - .matches(e -> e.getHttpCode() == 501) - .extracting(ODataServiceErrorException::getOdataError) - .satisfies(e -> { - assertThat(e.getODataCode()).isEqualTo("AB/100"); - assertThat(e.getODataMessage()).startsWith("Invalid method invocation: 'CREATE'"); - - final List details = e.getDetails(); - assertThat(details).isNotNull().hasSize(1); - assertThat(details.get(0).getODataCode()).isEqualTo("INNER-CODE-1"); - assertThat(details.get(0).getODataMessage()).isEqualTo("INNER-MESSAGE-1"); - }); - - verify(1, headRequestedFor(urlEqualTo(ODATA_SERVICE_PATH + "/"))); - verify(1, postRequestedFor(urlEqualTo(ODATA_SERVICE_PATH + "/" + ODATA_ENTITY_RELATED))); - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - @EqualsAndHashCode( callSuper = true ) - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class TestEntity extends VdmEntity - { - private final String entityCollection = ODATA_ENTITY; - private final Class type = TestEntity.class; - - @SerializedName( "Name" ) - @JsonProperty( "Name" ) - @ODataField( odataName = "Name" ) - @With - private String name; - - @SerializedName( RELATIONS_NAV_PROP ) - @JsonProperty( RELATIONS_NAV_PROP ) - @ODataField( odataName = RELATIONS_NAV_PROP ) - private List relations; - - public static final TestEntityLink TO_RELATIONS = new TestEntityLink<>(RELATIONS_NAV_PROP); - - @Nonnull - @Override - protected Map getKey() - { - return Collections.singletonMap("Name", getName()); - } - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - @EqualsAndHashCode( callSuper = true ) - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class RelatedEntity extends VdmEntity - { - private final String entityCollection = ODATA_ENTITY_RELATED; - private final Class type = RelatedEntity.class; - - @SerializedName( "Description" ) - @JsonProperty( "Description" ) - @ODataField( odataName = "Description" ) - private String description; - } - - public static class TestEntityLink> - extends - EntityLink, TestEntity, ObjectT> - { - public TestEntityLink( final String name ) - { - super(name); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/CustomFieldTypeConverterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/CustomFieldTypeConverterTest.java deleted file mode 100644 index 1291dac152..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/CustomFieldTypeConverterTest.java +++ /dev/null @@ -1,256 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.time.LocalDateTime; -import java.time.Month; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import javax.annotation.Nonnull; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalDateTimeCalendarConverter; -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; -import com.sap.cloud.sdk.typeconverter.exception.ObjectNotConvertibleException; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@WireMockTest -class CustomFieldTypeConverterTest -{ - private static final String ODATA_ENDPOINT_URL = "/service/path"; - private static final String ENTITY_SET = "A_TestEntity"; - private static final String MY_CUSTOM_FIELD = "MyCustomField"; - private static final String RESPONSE = """ - { - "d": { - "results": [ - { - "__metadata": { - "id": "https://127.0.0.1/service/path/A_TestEntity('1')", - "uri": "https://127.0.0.1/service/path/A_TestEntity('1')", - "type": "API_TEST.A_TestEntityType" - }, - "Id": "1", - "SomeField": "123", - "ETag": "SOME_ETAG", - "MyCustomField": %1$s - } - ] - } - } - """; - - private DefaultHttpDestination destination; - - @BeforeEach - void before( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - } - - @Data - @EqualsAndHashCode( callSuper = true ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) - public static class TestEntity extends VdmEntity - { - private final String entityCollection = ENTITY_SET; - private final Class type = TestEntity.class; - } - - @Getter - private static class LocalDateTimeBooleanConverter extends AbstractTypeConverter - { - private final Class type = LocalDateTime.class; - private final Class domainType = Boolean.class; - - @Nonnull - @Override - public ConvertedObject toDomainNonNull( @Nonnull final LocalDateTime object ) - throws Exception - { - throw new Exception("This implementation should fail."); - } - - @Nonnull - @Override - public ConvertedObject fromDomainNonNull( @Nonnull final Boolean domainObject ) - throws Exception - { - throw new Exception("This implementation should fail."); - } - } - - @Test - void calendarToLocalDateTimeOnGet() - { - mockResponses("\"/Date(1507075200000)/\""); - - final EntityField myCustomField = - new EntityField<>(MY_CUSTOM_FIELD, new LocalDateTimeCalendarConverter()); - - final TestEntity testEntity = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .read(TestEntity.class, ENTITY_SET) - .executeRequest(destination) - .get(0); - - final LocalDateTime customField = testEntity.getCustomField(myCustomField); - - assertThat(customField).isInstanceOf(LocalDateTime.class); - assertThat(customField).isEqualTo(LocalDateTime.of(2017, Month.OCTOBER, 4, 0, 0, 0)); - } - - @Test - void returnsNullOnNullValue() - { - mockResponses("null"); - final EntityField myCustomField = - new EntityField<>(MY_CUSTOM_FIELD, new LocalDateTimeCalendarConverter()); - - final TestEntity testEntity = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .read(TestEntity.class, ENTITY_SET) - .executeRequest(destination) - .get(0); - - final LocalDateTime customField = testEntity.getCustomField(myCustomField); - assertThat(customField).isNull(); - } - - @Test - void failsOnConversionErrorOnGet() - { - mockResponses("\"/Date(1507075200000)/\""); - final EntityField myCustomField = - new EntityField<>(MY_CUSTOM_FIELD, new LocalDateTimeBooleanConverter()); - - final TestEntity testEntity = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .read(TestEntity.class, ENTITY_SET) - .executeRequest(destination) - .get(0); - - assertThatThrownBy(() -> testEntity.getCustomField(myCustomField)) - .isExactlyInstanceOf(ObjectNotConvertibleException.class); - } - - // this test verifies backwards compatibility of the API changes - @Test - void entityFieldWithoutConverterStillWorksForGet() - { - mockResponses("\"/Date(1507075200000)/\""); - - final EntityField myCustomField = new EntityField<>(MY_CUSTOM_FIELD); - - final TestEntity TestEntity = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .read(TestEntity.class, ENTITY_SET) - .executeRequest(destination) - .get(0); - - final GregorianCalendar customField = TestEntity.getCustomField(myCustomField); - assertThat(customField).isNotNull(); - } - - @Test - void localDateTimeToCalendarOnSet() - { - final EntityField myCustomField = - new EntityField<>(MY_CUSTOM_FIELD, new LocalDateTimeCalendarConverter()); - - final TestEntity TestEntity = new TestEntity(); - TestEntity.setCustomField(myCustomField, LocalDateTime.of(2017, Month.OCTOBER, 4, 0, 0, 0)); - - final Object myCustomFieldValue = TestEntity.getCustomField(MY_CUSTOM_FIELD); - assertThat(myCustomFieldValue).isInstanceOf(GregorianCalendar.class); - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - calendar.clear(); - calendar.set(2017, Calendar.OCTOBER, 4, 0, 0, 0); - assertThat(myCustomFieldValue).isEqualTo(calendar); - } - - @Test - void setsNullOnNullValue() - { - final EntityField myCustomField = - new EntityField<>(MY_CUSTOM_FIELD, new LocalDateTimeCalendarConverter()); - - final TestEntity testEntity = new TestEntity(); - testEntity.setCustomField(myCustomField, null); - - final Object myCustomFieldValue = testEntity.getCustomField(MY_CUSTOM_FIELD); - assertThat(myCustomFieldValue).isNull(); - final LocalDateTime customField = testEntity.getCustomField(myCustomField); - assertThat(customField).isNull(); - } - - @Test - void failsOnConversionErrorOnSet() - { - final EntityField myCustomField = - new EntityField<>(MY_CUSTOM_FIELD, new LocalDateTimeBooleanConverter()); - - final TestEntity testEntity = new TestEntity(); - assertThatThrownBy( - () -> testEntity.setCustomField(myCustomField, LocalDateTime.of(2017, Month.OCTOBER, 4, 0, 0, 0))) - .isExactlyInstanceOf(ObjectNotConvertibleException.class); - } - - // this test verifies backwards compatibility of the API changes - @Test - void entityFieldWithoutConverterStillWorksForSet() - { - final EntityField myCustomField = new EntityField<>(MY_CUSTOM_FIELD); - - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - calendar.clear(); - calendar.set(2017, Calendar.OCTOBER, 4, 0, 0, 0); - - final TestEntity testEntity = new TestEntity(); - testEntity.setCustomField(myCustomField, calendar); - } - - @Test - void entityFieldHasNoTypeConverterWhenRetrievedWithoutTypeConverter() - { - final EntityField myCustomField = new EntityField<>(MY_CUSTOM_FIELD); - Assertions.assertThat(myCustomField.getTypeConverter()).isNull(); - } - - @Test - void entityFieldHasTypeConverterWhenRetrievedWithTypeConverter() - { - final EntityField myCustomField = - new EntityField<>(MY_CUSTOM_FIELD, new LocalDateTimeCalendarConverter()); - Assertions.assertThat(myCustomField.getTypeConverter()).isNotNull(); - Assertions.assertThat(myCustomField.getFieldName()).isEqualTo(MY_CUSTOM_FIELD); - } - - private void mockResponses( final String customFieldValue ) - { - final String mockedUrl = ODATA_ENDPOINT_URL + "/" + ENTITY_SET; - stubFor(get(urlEqualTo(mockedUrl)).willReturn(okJson(String.format(RESPONSE, customFieldValue)))); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperEtagParsingTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperEtagParsingTest.java deleted file mode 100644 index 9778c84228..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/FluentHelperEtagParsingTest.java +++ /dev/null @@ -1,368 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.HttpHeaders; -import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5CacheBuilder; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestinationProperties; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; - -import lombok.Getter; -import lombok.SneakyThrows; - -class FluentHelperEtagParsingTest -{ - private static final String SERVICE_PATH = "/service"; - private static final String ENTITY_COLLECTION = "/EntityCollection"; - private static final HttpDestinationProperties DESTINATION = DefaultHttpDestination.builder("http://1").build(); - private static final String FUNCTION_NAME = "FUNCTION_NAME"; - private static final String ETAG_HEAD = "foo"; - private static final String ETAG_BODY = "bar"; - private static final FluentHelperFactory FACTORY = FluentHelperFactory.withServicePath(SERVICE_PATH); - private static final Map HEADERS_NONE = Collections.emptyMap(); - private static final Map HEADERS_WITH_ETAG = Collections.singletonMap("Etag", ETAG_HEAD); - - private HttpClient httpClient; - - @BeforeEach - void setupConnectivity() - { - httpClient = mock(HttpClient.class); - ODataApacheHttpClient5Accessor - .setHttpClientCache(new ApacheHttpClient5CacheBuilder().durationInMilliseconds(0).build()); - ODataApacheHttpClient5Accessor.setHttpClientFactory(dest -> { - assertThat(dest).isSameAs(DESTINATION); - return httpClient; - }); - } - - @AfterEach - void teardownConnectivity() - { - ODataApacheHttpClient5Accessor - .setHttpClientCache(new ApacheHttpClient5CacheBuilder().duration(Duration.ofMinutes(5)).build()); - ODataApacheHttpClient5Accessor.setHttpClientFactory(null); - } - - /** - * ETag via HTTP header. - */ - @SneakyThrows - @Test - void testParseNoEtagGetByKey() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response("{d:{}}", HEADERS_NONE)); - - final TestEntity entity = - FACTORY - .readByKey(TestEntity.class, ENTITY_COLLECTION, Collections.singletonMap("key", "val")) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).isEmpty(); - } - - @SneakyThrows - @Test - void testParseNoEtagFunctionImport() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response(String.format("{d:{%s:{}}}", FUNCTION_NAME), HEADERS_NONE)); - - // GET - TestEntity entity = - FACTORY - .functionSingleGet(Collections.singletonMap("key", "val"), FUNCTION_NAME, TestEntity.class) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).isEmpty(); - - // POST - entity = - FACTORY - .functionSinglePost(Collections.singletonMap("key", "val"), FUNCTION_NAME, TestEntity.class) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).isEmpty(); - } - - @SneakyThrows - @Test - void testParseNoEtagUpdate() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response("{d:{}}", HEADERS_NONE)); - - final TestEntity requestEntity = new TestEntity(); - - final ModificationResponse result = - FACTORY.update(ENTITY_COLLECTION, requestEntity).executeRequest(DESTINATION); - - assertThat(result).isNotNull(); - assertThat(result.getModifiedEntity()).isNotNull(); - assertThat(result.getResponseEntity()).isNotNull().isNotEmpty(); - assertThat(result.getRequestEntity()).isNotNull().isSameAs(requestEntity); - - assertThat(result.getModifiedEntity().getVersionIdentifier()).isEmpty(); - assertThat(result.getResponseEntity().get().getVersionIdentifier()).isEmpty(); - assertThat(result.getRequestEntity().getVersionIdentifier()).isEmpty(); - } - - @SneakyThrows - @Test - void testParseHeaderEtagGetByKey() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response("{d:{}}", HEADERS_WITH_ETAG)); - - final TestEntity entity = - FACTORY - .readByKey(TestEntity.class, ENTITY_COLLECTION, Collections.singletonMap("key", "val")) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - } - - @SneakyThrows - @Test - void testParseHeaderEtagFunctionImport() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn(response(String.format("{d:{%s:{}}}", FUNCTION_NAME), HEADERS_WITH_ETAG)); - - // GET - TestEntity entity = - FACTORY - .functionSingleGet(Collections.singletonMap("key", "val"), FUNCTION_NAME, TestEntity.class) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - - // POST - entity = - FACTORY - .functionSinglePost(Collections.singletonMap("key", "val"), FUNCTION_NAME, TestEntity.class) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - } - - @SneakyThrows - @Test - void testParseHeaderEtagUpdate() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response("{d:{}}", HEADERS_WITH_ETAG)); - - final TestEntity requestEntity = new TestEntity(); - - final ModificationResponse result = - FACTORY.update(ENTITY_COLLECTION, requestEntity).executeRequest(DESTINATION); - - assertThat(result).isNotNull(); - assertThat(result.getModifiedEntity()).isNotNull(); - assertThat(result.getResponseEntity()).isNotNull().isNotEmpty(); - assertThat(result.getRequestEntity()).isNotNull().isSameAs(requestEntity); - - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_HEAD); - assertThat(result.getResponseEntity().get().getVersionIdentifier()).containsExactly(ETAG_HEAD); - assertThat(result.getRequestEntity().getVersionIdentifier()).isEmpty(); - } - - /** - * ETag via HTTP payload. - */ - @SneakyThrows - @Test - void testParsePayloadEtagGetAll() - { - final String payload = String.format("{d:{results:[{__metadata:{etag:\"%s\"}}]}}", ETAG_BODY); - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response(payload, HEADERS_NONE)); - - final List entities = FACTORY.read(TestEntity.class, ENTITY_COLLECTION).executeRequest(DESTINATION); - - assertThat(entities).isNotNull().hasSize(1); - assertThat(entities.get(0).getVersionIdentifier()).containsExactly(ETAG_BODY); - } - - @SneakyThrows - @Test - void testParsePayloadEtagGetByKey() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn(response(String.format("{d:{__metadata:{etag:\"%s\"}}}", ETAG_BODY), HEADERS_NONE)); - - final TestEntity entity = - FACTORY - .readByKey(TestEntity.class, ENTITY_COLLECTION, Collections.singletonMap("key", "val")) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_BODY); - } - - @SneakyThrows - @Test - void testParsePayloadEtagFunctionImport() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn( - response(String.format("{d:{%s:{__metadata:{etag:\"%s\"}}}}", FUNCTION_NAME, ETAG_BODY), HEADERS_NONE)); - - // GET - TestEntity entity = - FACTORY - .functionSingleGet(Collections.singletonMap("key", "val"), FUNCTION_NAME, TestEntity.class) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_BODY); - - // POST - entity = - FACTORY - .functionSinglePost(Collections.singletonMap("key", "val"), FUNCTION_NAME, TestEntity.class) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_BODY); - } - - @SneakyThrows - @Test - void testParsePayloadEtagUpdate() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn(response(String.format("{d:{__metadata:{etag:\"%s\"}}}", ETAG_BODY), HEADERS_NONE)); - - final TestEntity requestEntity = new TestEntity(); - - final ModificationResponse result = - FACTORY.update(ENTITY_COLLECTION, requestEntity).executeRequest(DESTINATION); - - assertThat(result).isNotNull(); - assertThat(result.getModifiedEntity()).isNotNull(); - assertThat(result.getResponseEntity()).isNotNull().isNotEmpty(); - assertThat(result.getRequestEntity()).isNotNull().isSameAs(requestEntity); - - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_BODY); - assertThat(result.getResponseEntity().get().getVersionIdentifier()).containsExactly(ETAG_BODY); - assertThat(result.getRequestEntity().getVersionIdentifier()).isEmpty(); - } - - /** - * ETag via HTTP header + HTTP payload. - */ - @SneakyThrows - @Test - void testParseHeaderAndPayloadEtagGetByKey() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn(response(String.format("{d:{__metadata:{etag:\"%s\"}}}", ETAG_BODY), HEADERS_WITH_ETAG)); - - final TestEntity entity = - FACTORY - .readByKey(TestEntity.class, ENTITY_COLLECTION, Collections.singletonMap("key", "val")) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - } - - @SneakyThrows - @Test - void testParseHeaderAndPayloadEtagFunctionImport() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn( - response( - String.format("{d:{%s:{__metadata:{etag:\"%s\"}}}}", FUNCTION_NAME, ETAG_BODY), - HEADERS_WITH_ETAG)); - - // GET - TestEntity entity = - FACTORY - .functionSingleGet(Collections.singletonMap("key", "val"), FUNCTION_NAME, TestEntity.class) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - - // POST - entity = - FACTORY - .functionSinglePost(Collections.singletonMap("key", "val"), FUNCTION_NAME, TestEntity.class) - .executeRequest(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - } - - @SneakyThrows - @Test - void testParseHeaderAndPayloadEtagUpdate() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn(response(String.format("{d:{__metadata:{etag:\"%s\"}}}", ETAG_BODY), HEADERS_WITH_ETAG)); - - final TestEntity requestEntity = new TestEntity(); - - final ModificationResponse result = - FACTORY.update(ENTITY_COLLECTION, requestEntity).executeRequest(DESTINATION); - - assertThat(result).isNotNull(); - assertThat(result.getModifiedEntity()).isNotNull(); - assertThat(result.getResponseEntity()).isNotNull().isNotEmpty(); - assertThat(result.getRequestEntity()).isNotNull().isSameAs(requestEntity); - - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_HEAD); - assertThat(result.getResponseEntity().get().getVersionIdentifier()).containsExactly(ETAG_HEAD); - assertThat(result.getRequestEntity().getVersionIdentifier()).isEmpty(); - } - - /** - * HELPER METHODS. - */ - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) - public static class TestEntity extends VdmEntity - { - @Getter - private final String entityCollection = ENTITY_COLLECTION; - @Getter - private final Class type = TestEntity.class; - } - - @Nonnull - static ClassicHttpResponse response( @Nonnull final String payload, @Nonnull final Map headers ) - { - final BasicClassicHttpResponse result = new BasicClassicHttpResponse(200, "Ok"); - result.setEntity(new StringEntity(payload, StandardCharsets.UTF_8)); - result.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()); - headers.forEach(result::setHeader); - return result; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/FunctionImportSingleDeserializationTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/FunctionImportSingleDeserializationTest.java deleted file mode 100644 index 3c088d4fa3..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/FunctionImportSingleDeserializationTest.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; -import java.util.stream.Stream; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.HttpHeaders; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@WireMockTest -class FunctionImportSingleDeserializationTest -{ - private static final String SERVICE_PATH = "/some/path/service"; - private static final String FUNCTION_NAME = "SomeFunction"; - - @ParameterizedTest - @MethodSource - void testDeserializeSingleResponse( @Nonnull final TestInput testInput, @Nonnull final WireMockRuntimeInfo wm ) - { - stub(testInput); - - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - final Object actualReturnValue = testInput.functionSingleGet().executeSingle(destination); - - if( testInput.expectedReturnValue == null ) { - assertThat(actualReturnValue).isNull(); - } else { - assertThat(actualReturnValue).isEqualTo(testInput.expectedReturnValue); - } - } - - @Nonnull - static Stream> testDeserializeSingleResponse() - { - return Stream - .of( - new TestInput<>( - String.class, - """ - - - - - - """, - "{\"d\":{\"" - + FUNCTION_NAME - + "\":\"" - + "\\n" - + "\\n" - + "\\n" - + "\\n" - + "\\n\"}}"), - new TestInput<>(Boolean.class, true, "{\"d\":{\"" + FUNCTION_NAME + "\":true}}"), - new TestInput<>(Long.class, 42L, "{\"d\":{\"" + FUNCTION_NAME + "\":\"42\"}}"), - new TestInput<>(Void.class, null, null), - new TestInput<>( - TestingEntity.class, - new TestingEntity("id", "text"), - "{\"d\":{\"" + FUNCTION_NAME + "\":{\"id\":\"id\",\"text\":\"text\"}}}")); - } - - private void stub( @Nonnull final TestInput testInput ) - { - stubFor(head(urlPathEqualTo(SERVICE_PATH)).willReturn(ok().withHeader("x-csrf-token", "some-csrf-token"))); - - final ResponseDefinitionBuilder responseBuilder; - if( testInput.serializedReturnValue == null ) { - responseBuilder = noContent(); - } else { - responseBuilder = - ok() - .withBody(testInput.serializedReturnValue) - .withHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.getMimeType()); - } - - stubFor(get(urlPathEqualTo(SERVICE_PATH + "/" + FUNCTION_NAME)).willReturn(responseBuilder)); - } - - @AllArgsConstructor - @Getter - private static class TestInput - { - @Nonnull - private Class returnValueType; - @Nullable - private T expectedReturnValue; - @Nullable - private String serializedReturnValue; - - @Nonnull - public FluentHelperFunction functionSingleGet() - { - return FluentHelperFactory - .withServicePath(SERVICE_PATH) - .functionSingleGet(Collections.emptyMap(), FUNCTION_NAME, returnValueType); - } - - @Override - public String toString() - { - return "Single Function import with " + returnValueType.getSimpleName() + " return type"; - } - } - - @EqualsAndHashCode( callSuper = true ) - @NoArgsConstructor - @AllArgsConstructor - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class TestingEntity extends VdmEntity - { - @Getter( AccessLevel.PROTECTED ) - private final String entityCollection = "TestingCollection"; - - @Getter - private final Class type = TestingEntity.class; - - @SerializedName( "id" ) - @ODataField( odataName = "id" ) - private String id; - - @SerializedName( "text" ) - @ODataField( odataName = "text" ) - private String text; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/FunctionImportSingleEntityTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/FunctionImportSingleEntityTest.java deleted file mode 100644 index 0d7b721f68..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/FunctionImportSingleEntityTest.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder.okForJson; -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import java.net.URI; -import java.util.Map; -import java.util.UUID; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.classic.methods.HttpUriRequest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.client.MappingBuilder; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.google.gson.JsonElement; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestinationProperties; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; - -import io.vavr.control.Option; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@WireMockTest -class FunctionImportSingleEntityTest -{ - private static final String TEST_SERVICE_PATH = "/some/service/path"; - private static final String TEST_FUNCTION_NAME = "Testing"; - private static final String CSRF = "secret"; - private static final TestingEntity TEST_ENTITY = new TestingEntity("hello", "world"); - - private HttpDestinationProperties destination; - - @BeforeEach - void assignMockedDestination( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - final Map> functionResult = - ImmutableMap.of("d", ImmutableMap.of(TEST_FUNCTION_NAME, TEST_ENTITY)); - stubFor(get(anyUrl()).willReturn(okForJson(functionResult))); - stubFor(head(anyUrl()).willReturn(ok().withHeader("x-csrf-token", CSRF))); - } - - @EqualsAndHashCode( callSuper = true ) - @NoArgsConstructor - @AllArgsConstructor - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) - public static class TestingEntity extends VdmEntity - { - @Getter( AccessLevel.PROTECTED ) - private final String entityCollection = "TestingCollection"; - - @Getter - private final Class type = TestingEntity.class; - - @SerializedName( "id" ) - @ODataField( odataName = "id" ) - private String id; - - @SerializedName( "text" ) - @ODataField( odataName = "text" ) - private String text; - } - - private static class SingleEntityFunctionImport - extends - FluentHelperFunction - { - @Getter - private final Map parameters = Maps.newHashMap(); - - @Getter - private final String functionName = TEST_FUNCTION_NAME; - - @Getter - private final Class entityClass = TestingEntity.class; - - public SingleEntityFunctionImport() - { - super(TEST_SERVICE_PATH); - } - - @Nullable - @Override - protected JsonElement refineJsonResponse( @Nullable final JsonElement jsonElement ) - { - return Option.of(jsonElement).toTry().map(o -> o.getAsJsonObject().get(getFunctionName())).getOrNull(); - } - - @Nonnull - @Override - protected HttpUriRequest createRequest( @Nonnull final URI uri ) - { - return new HttpGet(uri); - } - - @Nullable - @Override - public TestingEntity executeRequest( @Nonnull final Destination destination ) - { - return super.executeSingle(destination); - } - - private void addParameter( final String key, final Object value ) - { - parameters.put(key, value); - } - } - - @Test - void testFunctionParameterWithPlusSign() - { - final SingleEntityFunctionImport functionImport = new SingleEntityFunctionImport(); - functionImport.addParameter("foo", "ba+r"); - - final TestingEntity responseEntity = functionImport.executeRequest(destination); - - verify(getRequestedFor(urlEqualTo(TEST_SERVICE_PATH + "/Testing?foo='ba%2Br'"))); - assertThat(responseEntity).isEqualTo(TEST_ENTITY); - } - - @Test - void testFunctionParameterWithWhitespace() - { - final SingleEntityFunctionImport functionImport = new SingleEntityFunctionImport(); - functionImport.addParameter("foo", "b a+ r "); - - final TestingEntity responseEntity = functionImport.executeRequest(destination); - - verify(getRequestedFor(urlEqualTo(TEST_SERVICE_PATH + "/Testing?foo='b%20a%2B%20%20r%20'"))); - assertThat(responseEntity).isEqualTo(TEST_ENTITY); - } - - @Test - void testFunctionParameterWithQuotes() - { - final SingleEntityFunctionImport functionImport = new SingleEntityFunctionImport(); - functionImport.addParameter("GUID", UUID.fromString("571c74ab-c66a-4d34-ab32-ff16ec4653a5")); - - final TestingEntity responseEntity = functionImport.executeRequest(destination); - - verify( - getRequestedFor( - urlEqualTo(TEST_SERVICE_PATH + "/Testing?GUID=guid'571c74ab-c66a-4d34-ab32-ff16ec4653a5'"))); - assertThat(responseEntity).isEqualTo(TEST_ENTITY); - } - - @Test - void testFunctionParameterWithSpecialCharacters() - { - final SingleEntityFunctionImport functionImport = new SingleEntityFunctionImport(); - functionImport.addParameter("Address", "Unusual? Road #99 with 100%"); - - final TestingEntity responseEntity = functionImport.executeRequest(destination); - - verify( - getRequestedFor( - urlEqualTo(TEST_SERVICE_PATH + "/Testing?Address='Unusual%3F%20Road%20%2399%20with%20100%25'"))); - assertThat(responseEntity).isEqualTo(TEST_ENTITY); - } - - @Test - void testFunctionParameterWithParentheses() - { - final SingleEntityFunctionImport functionImport = new SingleEntityFunctionImport(); - functionImport.addParameter("Address", "Potsdam (Germany)"); - - final TestingEntity responseEntity = functionImport.executeRequest(destination); - - verify(getRequestedFor(urlEqualTo(TEST_SERVICE_PATH + "/Testing?Address='Potsdam%20(Germany)'"))); - assertThat(responseEntity).isEqualTo(TEST_ENTITY); - } - - @Test - void testCustomJsonResponseParsing() - { - final String jsonResponse = "{\"d\":{\"CUSTOM_OBJECT\":{\"id\":\"hello\",\"text\":\"world\"}}}"; - final MappingBuilder requestMapping = get(urlEqualTo(TEST_SERVICE_PATH + "/Testing")); - stubFor(requestMapping.willReturn(okJson(jsonResponse))); - - final SingleEntityFunctionImport originalFunctionImport = new SingleEntityFunctionImport() - { - @Nonnull - @Override - public String getFunctionName() - { - return "Testing"; - } - - @Override - protected JsonElement refineJsonResponse( @Nullable final JsonElement jsonElement ) - { - return jsonElement.getAsJsonObject().get("CUSTOM_OBJECT"); - } - }; - - final TestingEntity responseEntity = originalFunctionImport.executeRequest(destination); - assertThat(responseEntity).isEqualTo(TEST_ENTITY); - } - - @Test - void testFunctionParameterWithConventionalValue() - { - final SingleEntityFunctionImport functionImport = new SingleEntityFunctionImport(); - functionImport.addParameter("Address", "Potsdam"); - - final TestingEntity responseEntity = functionImport.executeRequest(destination); - - verify(getRequestedFor(urlEqualTo(TEST_SERVICE_PATH + "/Testing?Address='Potsdam'"))); - assertThat(responseEntity).isEqualTo(TEST_ENTITY); - } - - @Test - void testFunctionImportWithoutParameters() - { - final SingleEntityFunctionImport functionImport = new SingleEntityFunctionImport(); - - final TestingEntity responseEntity = functionImport.executeRequest(destination); - - verify(getRequestedFor(urlEqualTo(TEST_SERVICE_PATH + "/Testing"))); - assertThat(responseEntity).isEqualTo(TEST_ENTITY); - } - - @Test - void testFunctionImportReturnsNullAsResponseBody() - { - final SingleEntityFunctionImport functionImport = new SingleEntityFunctionImport(); - - stubFor(get(urlEqualTo(TEST_SERVICE_PATH + "/Testing")).willReturn(null)); - - assertThatExceptionOfType(ODataException.class) - .isThrownBy(() -> functionImport.executeRequest(destination)) - .withMessageContaining("Unable to read OData 2.0 response."); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/HttpResponseEvaluationTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/HttpResponseEvaluationTest.java deleted file mode 100644 index af651e7f53..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/HttpResponseEvaluationTest.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import static org.apache.hc.core5.http.ContentType.APPLICATION_JSON; -import static org.apache.hc.core5.http.ContentType.TEXT_PLAIN; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; -import java.util.Map; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.client5.http.classic.methods.HttpUriRequest; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.HttpStatus; -import org.apache.hc.core5.http.io.entity.InputStreamEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; - -import lombok.SneakyThrows; - -public class HttpResponseEvaluationTest -{ - private static final Destination DESTINATION = DefaultHttpDestination.builder("foo").build(); - - private HttpClient httpClient; - private BasicClassicHttpResponse httpResponse; - private InputStreamEntity httpEntity; - private InputStream inputStream; - - @SneakyThrows - void mockHttpResponse( final ContentType contentType, final String payload ) - { - httpClient = mock(HttpClient.class); - inputStream = spy(new ByteArrayInputStream(payload.getBytes(UTF_8))); - httpEntity = spy(new InputStreamEntity(inputStream, contentType)); - httpResponse = spy(new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK")); - httpResponse.setEntity(httpEntity); - ODataApacheHttpClient5Accessor.setHttpClientFactory(destination -> httpClient); - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(httpResponse); - } - - @AfterEach - void teardown() - { - ODataApacheHttpClient5Accessor.setHttpClientFactory(null); - ODataApacheHttpClient5Accessor.setHttpClientCache(null); - } - - @SneakyThrows - @Test - void testCreate() - { - mockHttpResponse(APPLICATION_JSON, "{\"d\": {}}"); - - final ModificationResponse result = - FluentHelperFactory.withServicePath("/path").create(new TestVdmEntity()).executeRequest(DESTINATION); - - verify(httpClient, times(1)).executeOpen(isNull(), any(HttpUriRequest.class), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - - assertThat(result.getResponseStatusCode()).isEqualTo(200); - } - - @SneakyThrows - @Test - void testUpdate() - { - mockHttpResponse(APPLICATION_JSON, "{\"d\": {}}"); - - final TestVdmEntity testEntity = new TestVdmEntity(); - testEntity.setIntegerValue(42); - - final ModificationResponse result = - FluentHelperFactory.withServicePath("/path").update(testEntity).executeRequest(DESTINATION); - - verify(httpClient, times(1)).executeOpen(isNull(), any(HttpUriRequest.class), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - - assertThat(result.getResponseStatusCode()).isEqualTo(200); - } - - @SneakyThrows - @Test - void testDelete() - { - mockHttpResponse(APPLICATION_JSON, "{\"d\": {}}"); - - final ModificationResponse result = - FluentHelperFactory.withServicePath("/path").delete(new TestVdmEntity()).executeRequest(DESTINATION); - - verify(httpClient, times(1)).executeOpen(isNull(), any(HttpUriRequest.class), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - - assertThat(result.getResponseStatusCode()).isEqualTo(200); - } - - @SneakyThrows - @Test - void testReadAll() - { - mockHttpResponse(APPLICATION_JSON, "{\"d\": {\"results\": []}}"); - - final List result = - FluentHelperFactory - .withServicePath("/path") - .read(TestVdmEntity.class, "TestEntitySet") - .executeRequest(DESTINATION); - - assertThat(result).isNotNull(); - - verify(httpClient, times(1)).executeOpen(isNull(), any(HttpUriRequest.class), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - } - - @SneakyThrows - @Test - void testReadByKey() - { - mockHttpResponse(APPLICATION_JSON, "{\"d\":{}}"); - - final TestVdmEntity result = - FluentHelperFactory - .withServicePath("/path") - .readByKey(TestVdmEntity.class, "TestEntitySet", Map.of("IntegerValue", 42)) - .executeRequest(DESTINATION); - - assertThat(result).isNotNull(); - - verify(httpClient, times(1)).executeOpen(isNull(), any(HttpUriRequest.class), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - } - - @SneakyThrows - @Test - void testFunction() - { - mockHttpResponse(APPLICATION_JSON, "{\"d\": {\"results\": []}}"); - - final List result = - FluentHelperFactory - .withServicePath("/path") - .functionMultipleGet(Map.of("para", "meter"), "functionName", String.class) - .executeRequest(DESTINATION); - - assertThat(result).isNotNull(); - - verify(httpClient, times(1)).executeOpen(isNull(), any(HttpUriRequest.class), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - } - - @SneakyThrows - @Test - void testReadCount() - { - mockHttpResponse(TEXT_PLAIN, "42"); - - final long result = - FluentHelperFactory - .withServicePath("/path") - .read(TestVdmEntity.class, "TestEntitySet") - .count() - .executeRequest(DESTINATION); - - verify(httpClient, times(1)).executeOpen(isNull(), any(HttpUriRequest.class), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ModificationResponseTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ModificationResponseTest.java deleted file mode 100644 index c5899778ed..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ModificationResponseTest.java +++ /dev/null @@ -1,175 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import static com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol.V4; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpStatus; -import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.apache.hc.core5.http.message.BasicHeader; -import org.junit.jupiter.api.Test; - -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestUpdate; -import com.sap.cloud.sdk.datamodel.odata.client.request.UpdateStrategy; -import com.sap.cloud.sdk.result.ElementName; - -import io.vavr.control.Option; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.SneakyThrows; -import lombok.ToString; - -class ModificationResponseTest -{ - private static final String SERVICE_PATH = "/service-path"; - - private static final Destination destination = mock(Destination.class); - - @Data - @NoArgsConstructor - @AllArgsConstructor - @ToString( doNotUseGetters = true, callSuper = true ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) - public static class TestObject extends VdmEntity - { - @Getter - private final String odataType = "TestObject"; - - @Getter - private final Class type = TestObject.class; - - @ElementName( "foo" ) - private String name; - - @Nonnull - @Override - protected String getEntityCollection() - { - return odataType; - } - } - - @Test - void testEntityResponse() - { - final TestObject inputObject = new TestObject(); - - final ODataRequestGeneric request = mock(ODataRequestGeneric.class); - when(request.getProtocol()).thenReturn(ODataProtocol.V4); - - when(request.getServicePath()).thenReturn(SERVICE_PATH); - - final Header[] responseHeaders = { new BasicHeader("fizz", "buzz"), new BasicHeader("fizz", "fuzz, bizz=1") }; - - final ClassicHttpResponse response = mock(ClassicHttpResponse.class); - doReturn(responseHeaders).when(response).getHeaders(); - doReturn(responseHeaders).when(response).getHeaders("ETag"); - doReturn(new StringEntity("{\"foo\":\"bar\"}", UTF_8)).when(response).getEntity(); - doReturn(HttpStatus.SC_OK).when(response).getCode(); - - final ODataRequestResultGeneric result = new ODataRequestResultGeneric(request, response); - final ModificationResponse modification = ModificationResponse.of(result, inputObject, destination); - - assertThat(modification).isNotNull(); - assertThat(modification.getResponseStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertThat(modification.getRequestEntity()).isSameAs(inputObject); - - assertThat(modification.getResponseEntity().get()).isNotSameAs(inputObject); - assertThat(modification.getResponseEntity().get()).isEqualTo(new TestObject("bar")); - assertThat(modification.getModifiedEntity()).isEqualTo(new TestObject("bar")); - - assertThat(modification.getModifiedEntity().getDestinationForFetch()).isSameAs(destination); - assertThat(modification.getModifiedEntity().getServicePathForFetch()).isEqualTo(SERVICE_PATH); - - assertThat(modification.getResponseHeaders()).containsOnlyKeys("fizz"); - assertThat(modification.getResponseHeaders().get("fizz")).containsExactly("buzz", "fuzz, bizz=1"); - } - - @Test - void testEmptyResponse() - { - final TestObject inputObject = new TestObject(); - - final ODataRequestGeneric request = mock(ODataRequestGeneric.class); - when(request.getProtocol()).thenReturn(ODataProtocol.V4); - - when(request.getServicePath()).thenReturn(SERVICE_PATH); - - final ClassicHttpResponse response = mock(ClassicHttpResponse.class); - doReturn(new Header[0]).when(response).getHeaders(); - doReturn(new Header[0]).when(response).getHeaders("ETag"); - doReturn(new StringEntity("", UTF_8)).when(response).getEntity(); - doReturn(HttpStatus.SC_NO_CONTENT).when(response).getCode(); - - final ODataRequestResultGeneric result = new ODataRequestResultGeneric(request, response); - final ModificationResponse modification = ModificationResponse.of(result, inputObject, destination); - - assertThat(modification).isNotNull(); - assertThat(modification.getResponseStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - assertThat(modification.getRequestEntity()).isSameAs(inputObject); - assertThat(modification.getModifiedEntity()).isNotSameAs(inputObject); - assertThat(modification.getModifiedEntity().getDestinationForFetch()).isSameAs(destination); - assertThat(modification.getModifiedEntity().getServicePathForFetch()).isEqualTo(SERVICE_PATH); - assertThat(modification.getModifiedEntity()).isEqualTo(inputObject); - assertThat(modification.getResponseHeaders()).isEmpty(); - } - - @SneakyThrows - @Test - void testResponseIsOnlyEvaluatedOnce() - { - final TestObject inputObject = new TestObject(); - - final ClassicHttpResponse response = spy(new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK")); - response.setHeaders(new Header[0]); - response.setEntity(new StringEntity("{\"foo\":\"bar\"}", UTF_8)); - - final ODataEntityKey key = ODataEntityKey.of(Map.of("id", 42), V4); - final ODataRequestUpdate request = - new ODataRequestUpdate("service/path", "EntitySet", key, "{}", UpdateStrategy.REPLACE_WITH_PUT, null, V4); - - final HttpClient httpClient = mock(HttpClient.class); - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response); - - final ODataRequestResultGeneric result = request.execute(httpClient); - final ModificationResponse modification = ModificationResponse.of(result, inputObject, destination); - - modification.getResponseEntity(); - final Option responseEntity = modification.getResponseEntity(); - assertThat(responseEntity).isNotNull(); - - modification.getModifiedEntity(); - final TestObject modifiedEntity = modification.getModifiedEntity(); - assertThat(modifiedEntity).isNotNull(); - - verify(response, times(1)).getEntity(); - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataBatchRequestTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataBatchRequestTest.java deleted file mode 100644 index 72a6350462..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataBatchRequestTest.java +++ /dev/null @@ -1,436 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.containing; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.Function; -import java.util.stream.Collectors; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.common.collect.Lists; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; -import com.sap.cloud.sdk.datamodel.odata.helper.batch.BatchChangeSetFluentHelperBasic; -import com.sap.cloud.sdk.datamodel.odata.helper.batch.BatchFluentHelperBasic; -import com.sap.cloud.sdk.datamodel.odata.helper.batch.BatchResponse; -import com.sap.cloud.sdk.datamodel.odata.helper.batch.BatchResponseChangeSet; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory; - -import io.vavr.control.Try; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.With; - -@WireMockTest -class ODataBatchRequestTest -{ - private static final String ODATA_SERVICE_PATH = "/service/path"; - - private static String multipartCreatedEntity( final String key, final String description ) - { - final String url = String.format("https://127.0.0.1/service/path/A_TestEntity('%s')", key); - final String metadata = String.format("{\"id\":\"%s\",\"uri\":\"%s\",\"type\":\"A_TestEntityType\"}", url, url); - return "Content-Type: application/http\r\n" - + "Content-Length: 2811\r\n" - + "content-transfer-encoding: binary\r\n" - + "\r\n" - + "HTTP/1.1 201 Created\r\n" - + "Content-Type: application/json\r\n" - + "Content-Length: 2553\r\n" - + ("location: " + url + ")\r\n") - + "dataserviceversion: 2.0\r\n" - + "\r\n" - + "{" - + ("\"d\":{\"__metadata\":" + metadata + ",") - + ("\"Name\":\"" + key + "\",") - + ("\"GeneratedProperty\":\"" + UUID.randomUUID() + "'\",") - + ("\"Description\":\"" + description + "\"") - + "}}\r\n"; - } - - private static String multipartNoContent() - { - return """ - Content-Type: application/http - Content-Length: 71 - content-transfer-encoding: binary - - HTTP/1.1 204 No Content - Content-Length: 0 - dataserviceversion: 2.0 - """; - } - - private static final String SINGLE_CHANGESET_RESPONSE_MULTIPART = - "--FB687BFCC8917ABB09014537111216650\r\n" - + "Content-Type: multipart/mixed; boundary=FB687BFCC8917ABB09014537111216651\r\n" - + "Content-Length: 5921\r\n" // ignored - + "\r\n" - + "--FB687BFCC8917ABB09014537111216651\r\n" - + multipartCreatedEntity("501", "Same description") - + "--FB687BFCC8917ABB09014537111216651\r\n" - + multipartCreatedEntity("502", "Same description") - + "--FB687BFCC8917ABB09014537111216651--\r\n" - + "\r\n" - + "--FB687BFCC8917ABB09014537111216650--"; - - private static final String TWO_CHANGESETS_RESPONSE_MULTIPART = - "--9955BD2AAB53BE8D34D8913DDB06697B0\r\n" - + "Content-Type: multipart/mixed; boundary=9955BD2AAB53BE8D34D8913DDB06697B1\r\n" - + "Content-Length: 2980\r\n" // ignored - + "\r\n" - + "--9955BD2AAB53BE8D34D8913DDB06697B1\r\n" - + multipartCreatedEntity("503", "Alternate description") - + "--9955BD2AAB53BE8D34D8913DDB06697B1--\r\n" - + "\r\n" - + "--9955BD2AAB53BE8D34D8913DDB06697B0\r\n" - + "Content-Type: multipart/mixed; boundary=9955BD2AAB53BE8D34D8913DDB06697B1\r\n" - + "Content-Length: 2980\r\n" // ignored - + "\r\n" - + "--9955BD2AAB53BE8D34D8913DDB06697B1\r\n" - + multipartCreatedEntity("504", "Strange description") - + "--9955BD2AAB53BE8D34D8913DDB06697B1--\r\n" - + "\r\n" - + "--9955BD2AAB53BE8D34D8913DDB06697B0--"; - - private static final String SINGLE_UPDATE_DELETE_CHANGESET_RESPONSE_MULTIPART = - "--4729730C52B1E9D05AAAB969F2FEE6970\r\n" - + "Content-Type: multipart/mixed; boundary=4729730C52B1E9D05AAAB969F2FEE6971\r\n" - + "Content-Length: 437\r\n" // ignored - + "\r\n" - + "--4729730C52B1E9D05AAAB969F2FEE6971\r\n" - + multipartNoContent() - + "\r\n" - + "\r\n" - + "--4729730C52B1E9D05AAAB969F2FEE6971\r\n" - + multipartNoContent() - + "\r\n" - + "\r\n" - + "--4729730C52B1E9D05AAAB969F2FEE6971--\r\n" - + "\r\n" - + "--4729730C52B1E9D05AAAB969F2FEE6970--"; - - private HttpDestination destination; - - @BeforeEach - void before( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - stubFor(head(urlEqualTo(ODATA_SERVICE_PATH)).willReturn(ok())); - } - - @Test - void testTwoCreatesInOneChangeSet() - { - // prepare mocked data - final ResponseDefinitionBuilder response = - okForContentType( - "multipart/mixed; boundary=FB687BFCC8917ABB09014537111216650", - SINGLE_CHANGESET_RESPONSE_MULTIPART); - stubFor(post(urlEqualTo(ODATA_SERVICE_PATH + "/$batch")).willReturn(response)); - - final TestEntity address1 = new TestEntity(); - address1.setDescription("Same description"); - - final TestEntity address2 = new TestEntity(); - address2.setDescription("Same description"); - - final BatchResponse result = - new TestBatch() - .beginChangeSet() - .createTestEntity(address1) - .createTestEntity(address2) - .endChangeSet() - .executeRequest(destination); - - final Try changeSet = result.get(0); - assertThat(changeSet.isSuccess()).isTrue(); - - final List> createdEntities = changeSet.get().getCreatedEntities(); - assertResponse(createdEntities, 2, address1); - } - - @Test - void testTwoCreatesInMultipleChangeSets() - { - // prepare mocked data - final ResponseDefinitionBuilder response = - okForContentType( - "multipart/mixed; boundary=9955BD2AAB53BE8D34D8913DDB06697B0", - TWO_CHANGESETS_RESPONSE_MULTIPART); - stubFor(post(urlEqualTo(ODATA_SERVICE_PATH + "/$batch")).willReturn(response)); - - final TestEntity address1 = new TestEntity(); - address1.setDescription("Alternate description"); - - final TestEntity address2 = new TestEntity(); - address2.setDescription("Strange description"); - - final BatchResponse result = - new TestBatch() - - .beginChangeSet() - .createTestEntity(address1) - .endChangeSet() - - .beginChangeSet() - .createTestEntity(address2) - .endChangeSet() - - .executeRequest(destination); - - assertThat(result.get(0)).isNotEmpty(); - assertResponse(result.get(0).get().getCreatedEntities(), 1, address1); - - assertThat(result.get(1)).isNotEmpty(); - assertResponse(result.get(1).get().getCreatedEntities(), 1, address2); - } - - @Test - void testUpdateDeleteOperationInOneChangeSet() - { - final String concreteNameEndpoint = "A_TestEntity(%27Test%27)"; - - final TestEntity address = new TestEntity().withName("Test"); - - // prepare mocked data - final ResponseDefinitionBuilder response = - okForContentType( - "multipart/mixed; boundary=4729730C52B1E9D05AAAB969F2FEE6970", - SINGLE_UPDATE_DELETE_CHANGESET_RESPONSE_MULTIPART); - stubFor( - post(urlEqualTo(ODATA_SERVICE_PATH + "/$batch")) - .withRequestBody(containing("PATCH " + concreteNameEndpoint)) - .withRequestBody(containing("DELETE " + concreteNameEndpoint)) - .willReturn(response)); - - // change entity - address.setDescription("Alternative Description"); - - final BatchResponse result = - new TestBatch() - .beginChangeSet() - .updateTestEntity(address) - .deleteTestEntity(address) - .endChangeSet() - .executeRequest(destination); - - final Try changeSet = result.get(0); - assertThat(changeSet.isSuccess()).isTrue(); - } - - @Test - void testUpdateWithPatchRetainingNullValues() - { - final String concreteNameEndpoint = "A_TestEntity(%27Test%27)"; - - final TestEntity address = new TestEntity().withName("Test").withDescription("Existing description"); - - // prepare mocked data - final ResponseDefinitionBuilder response = - okForContentType( - "multipart/mixed; boundary=4729730C52B1E9D05AAAB969F2FEE6970", - SINGLE_UPDATE_DELETE_CHANGESET_RESPONSE_MULTIPART); - stubFor( - post(urlEqualTo(ODATA_SERVICE_PATH + "/$batch")) - .withRequestBody(containing("PATCH " + concreteNameEndpoint)) - .willReturn(response)); - - // change entity - address.setDescription(null); - final BatchResponse result = - new TestBatch().beginChangeSet().updateTestEntity(address).endChangeSet().executeRequest(destination); - - verify( - postRequestedFor(urlEqualTo(ODATA_SERVICE_PATH + "/$batch")) - .withRequestBody(containing("\"Description\":" + null))); - final Try changeSet = result.get(0); - assertThat(changeSet.isSuccess()).isTrue(); - } - - /* - * Assertion helpers - */ - private void assertResponse( List> createdEntities, int expectedSize, TestEntity referenceEntity ) - { - // general assertion: two created entities not equal to each other or to the input instance - assertThat(createdEntities).hasSize(expectedSize).doesNotHaveDuplicates().doesNotContain((VdmEntity) null); - assertThat(createdEntities).allMatch(TestEntity.class::isInstance).doesNotContain(referenceEntity); - - // cast items to TestEntity - final List createdTestEntities = - createdEntities.stream().map(TestEntity.class::cast).collect(Collectors.toList()); - - // check for auto generated fields ID and UUID, must not be similar or equal to input - assertThat(createdTestEntities) - .extracting(TestEntity::getName) - .isNotEmpty() - .doesNotHaveDuplicates() - .doesNotContain(referenceEntity.getName(), "", null); - - assertThat(createdTestEntities) - .extracting(TestEntity::getGeneratedProperty) - .isNotEmpty() - .doesNotHaveDuplicates() - .doesNotContain(referenceEntity.getGeneratedProperty()); - - // check for valid input data - for( final Function handler : Lists - .> newArrayList(TestEntity::getDescription) ) { - assertThat(createdTestEntities).extracting(handler).containsOnly(handler.apply(referenceEntity)); - } - } - - @Test - void testEncodingInBatchedUpdate() - { - final ResponseDefinitionBuilder response = - okForContentType( - "multipart/mixed; boundary=FB687BFCC8917ABB09014537111216650", - SINGLE_CHANGESET_RESPONSE_MULTIPART); - stubFor( - post(urlEqualTo(ODATA_SERVICE_PATH + "/$batch")) - .withRequestBody(containing("Müller Straße")) - .willReturn(response)); - - final TestEntity address = new TestEntity().withName("Müller Straße"); - address.setDescription("Müller Straße"); - - new TestBatch().beginChangeSet().updateTestEntity(address).endChangeSet().executeRequest(destination); - - verify(1, postRequestedFor(urlEqualTo(ODATA_SERVICE_PATH + "/$batch"))); - verify(1, headRequestedFor(anyUrl()).withHeader("x-csrf-token", equalTo("fetch"))); - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - @EqualsAndHashCode( callSuper = true ) - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class TestEntity extends VdmEntity - { - private final String entityCollection = "A_TestEntity"; - private final Class type = TestEntity.class; - - @SerializedName( "Name" ) - @JsonProperty( "Name" ) - @ODataField( odataName = "Name" ) - @With - private String name; - - @SerializedName( "Description" ) - @JsonProperty( "Description" ) - @ODataField( odataName = "Description" ) - @With - private String description; - - @SerializedName( "GeneratedProperty" ) - @JsonProperty( "GeneratedProperty" ) - @ODataField( odataName = "GeneratedProperty" ) - private String generatedProperty; - - @Nonnull - @Override - protected Map getKey() - { - return Collections.singletonMap("Name", getName()); - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map result = new LinkedHashMap<>(); - result.put("Name", getName()); - result.put("Description", getDescription()); - return result; - } - - public void setDescription( final String description ) - { - rememberChangedField("Description", this.description); - this.description = description; - } - } - - @Getter - public static class TestBatch extends BatchFluentHelperBasic - { - private final String servicePathForBatchRequest = ODATA_SERVICE_PATH; - - @Nonnull - @Override - protected TestBatch getThis() - { - return this; - } - - @Nonnull - @Override - public TestBatchChangeset beginChangeSet() - { - return new TestBatchChangeset(this); - } - } - - public static class TestBatchChangeset extends BatchChangeSetFluentHelperBasic - { - public TestBatchChangeset( final TestBatch parent ) - { - super(parent, parent); - } - - @Nonnull - @Override - protected TestBatchChangeset getThis() - { - return this; - } - - public TestBatchChangeset updateTestEntity( final TestEntity entity ) - { - return super.addRequestUpdate(FluentHelperFactory.withServicePath(ODATA_SERVICE_PATH)::update, entity); - } - - public TestBatchChangeset createTestEntity( final TestEntity entity ) - { - return super.addRequestCreate(FluentHelperFactory.withServicePath(ODATA_SERVICE_PATH)::create, entity); - } - - public TestBatchChangeset deleteTestEntity( final TestEntity entity ) - { - return super.addRequestDelete(FluentHelperFactory.withServicePath(ODATA_SERVICE_PATH)::delete, entity); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataChangedFieldsTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataChangedFieldsTest.java deleted file mode 100644 index b25d5fcd0f..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataChangedFieldsTest.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -class ODataChangedFieldsTest -{ - @Test - void testEmpty() - { - final TestVdmEntity testEntity = new TestVdmEntity(); - assertThat(testEntity.getChangedFields()).isEmpty(); - } - - @Test - void testBuilder() - { - final TestVdmEntity testEntity = TestVdmEntity.builder().stringValue("Foo").build(); - assertThat(testEntity.getChangedFields()).isEmpty(); - } - - @Test - void testAccessor() - { - final TestVdmEntity testEntity = new TestVdmEntity(); - testEntity.setStringValue("Foo"); - assertThat(testEntity.getChangedFields()).containsEntry("StringValue", "Foo"); - } - - @Test - void testAccessorNull() - { - final TestVdmEntity testEntity = new TestVdmEntity(); - testEntity.setDecimalValue(null); - assertThat(testEntity.getChangedFields()).isEmpty(); - } - - @Test - void testCustomFields() - { - final TestVdmEntity testEntity = new TestVdmEntity(); - testEntity.setCustomField("BarValue", "Foo"); - assertThat(testEntity.getChangedFields()).containsEntry("BarValue", "Foo"); - } - - @Test - void testCustomFieldsNull() - { - final TestVdmEntity testEntity = new TestVdmEntity(); - testEntity.setCustomField("BarValue", null); - assertThat(testEntity.getChangedFields()).isEmpty(); - } - - @Test - void testRevertingValue() - { - final TestVdmEntity testEntity = new TestVdmEntity(); - testEntity.setIntegerValue(9000); - assertThat(testEntity.getChangedFields()).containsEntry("IntegerValue", 9000); - - testEntity.setIntegerValue(null); - assertThat(testEntity.getChangedFields()).isEmpty(); - } - - @Test - void testComplexValue() - { - final TestVdmEntity testEntity = new TestVdmEntity(); - final TestVdmComplex complex1 = TestVdmComplex.builder().someValue("Foo").build(); - final TestVdmComplex complex2 = new TestVdmComplex(); - complex2.setSomeValue("Bar"); - - testEntity.setComplexValue(complex1); - assertThat(testEntity.getChangedFields()).containsOnly(entry("ComplexValue", complex1)); - - testEntity.setComplexValue(complex2); - assertThat(testEntity.getChangedFields()).containsOnly(entry("ComplexValue", complex2)); - } - - @Disabled( "Not yet implemented. See CLOUDECOSYSTEM-9065" ) - @Test - void testInnerComplexValue() - { - final TestVdmComplex complex = TestVdmComplex.builder().someValue("Tic").build(); - final TestVdmEntity testEntity = TestVdmEntity.builder().complexValue(complex).build(); - assertThat(testEntity.getChangedFields()).isEmpty(); - - complex.setSomeValue("Tac"); - assertThat(testEntity.getChangedFields()).containsOnly(entry("ComplexValue", complex)); - } - - /** - * Updating changes on related entities in navigation properties are not supported in OData V2 (aka Deep Update). - */ - @Test - void testNavigationPropertyToOne() - { - final TestVdmEntity testEntity = new TestVdmEntity(); - final TestVdmEntity parent = TestVdmEntity.builder().stringValue("Foo").build(); - testEntity.setToParent(parent); - assertThat(testEntity.getChangedFields()).isEmpty(); - } - - /** - * Updating changes on related entities in navigation properties are not supported in OData V2 (aka Deep Update). - */ - @Test - void testNavigationPropertyToMany() - { - final TestVdmEntity testEntity = new TestVdmEntity(); - final TestVdmEntity child1 = TestVdmEntity.builder().stringValue("Foo").build(); - testEntity.addToChildren(child1); - assertThat(testEntity.getChangedFields()).isEmpty(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataEntitySerializerTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataEntitySerializerTest.java deleted file mode 100644 index 681b6da9ea..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataEntitySerializerTest.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; - -class ODataEntitySerializerTest -{ - @Test - void testSerializeEntityForCreate() - { - final TestVdmEntity entity = TestVdmEntity.builder().stringValue("string").booleanValue(false).build(); - entity.setIntegerValue(42); - - final String payload = ODataEntitySerializer.serializeEntityForCreate(entity); - assertThat(payload).isEqualTo("{\"IntegerValue\":42,\"StringValue\":\"string\",\"BooleanValue\":false}"); - } - - @Test - void testSerializeEntityForUpdatePut() - { - final TestVdmEntity entity = TestVdmEntity.builder().stringValue("string").booleanValue(false).build(); - entity.setIntegerValue(42); - - final String payload = ODataEntitySerializer.serializeEntityForUpdatePut(entity, null); - assertThat(payload) - .isEqualTo( - "{\"IntegerValue\":42,\"GuidValue\":null,\"StringValue\":\"string\",\"OffsetDateTimeValue\":null,\"to_Parent\":null,\"to_Children\":null,\"DecimalValue\":null,\"DoubleValue\":null,\"LocalTimeValue\":null,\"LocalDateTimeValue\":null,\"BooleanValue\":false,\"ComplexValue\":null}"); - } - - @Test - void testSerializeEntityForUpdatePutWithExcludedFields() - { - final TestVdmEntity entity = TestVdmEntity.builder().stringValue("NewString").booleanValue(true).build(); - entity.setIntegerValue(45); - - final List fieldsToExclude = - Arrays.asList(FieldReference.of("DoubleValue"), FieldReference.of("ComplexValue")); - final String payload = ODataEntitySerializer.serializeEntityForUpdatePut(entity, fieldsToExclude); - assertThat(payload) - .isEqualTo( - "{\"IntegerValue\":45,\"GuidValue\":null,\"StringValue\":\"NewString\",\"OffsetDateTimeValue\":null,\"to_Parent\":null,\"to_Children\":null,\"DecimalValue\":null,\"LocalTimeValue\":null,\"LocalDateTimeValue\":null,\"BooleanValue\":true}"); - } - - @Test - void testSerializeEntityForUpdatePatch() - { - final TestVdmEntity entity = TestVdmEntity.builder().stringValue("string").booleanValue(false).build(); - entity.setIntegerValue(42); - - final Collection fields = Arrays.asList(FieldReference.of("a"), FieldReference.of("b")); - final String payload = ODataEntitySerializer.serializeEntityForUpdatePatchShallow(entity, fields); - assertThat(payload).isEqualTo("{\"a\":null,\"b\":null,\"IntegerValue\":42}"); - } - - @Test - void testSerializeEntityForUpdatePatchNested() - { - final TestVdmComplex grandchildComplex = TestVdmComplex.builder().someValue("initialGrandchildValue").build(); - final TestVdmComplex childComplex = - TestVdmComplex.builder().someValue("initialChildValue").complexValue(grandchildComplex).build(); - final TestVdmEntity rootEntity = - TestVdmEntity - .builder() - .stringValue("initialRootValue") - .booleanValue(false) - .complexValue(childComplex) - .build(); - - rootEntity.setStringValue("newRootValue"); - grandchildComplex.setSomeValue("newGrandchildValue"); - - final Collection additionalFields = Arrays.asList(FieldReference.of("customField")); - - final String fullPayload = - ODataEntitySerializer.serializeEntityForUpdatePatchRecursiveFull(rootEntity, additionalFields); - assertThat(fullPayload) - .isEqualTo( - "{\"StringValue\":\"newRootValue\",\"customField\":null,\"ComplexValue\":{\"SomeValue\":\"initialChildValue\",\"OtherValue\":null,\"ComplexValue\":{\"SomeValue\":\"newGrandchildValue\",\"OtherValue\":null,\"ComplexValue\":null}}}"); - - final String deltaPayload = - ODataEntitySerializer.serializeEntityForUpdatePatchRecursiveDelta(rootEntity, additionalFields); - assertThat(deltaPayload) - .isEqualTo( - "{\"customField\":null,\"ComplexValue\":{\"ComplexValue\":{\"SomeValue\":\"newGrandchildValue\"}},\"StringValue\":\"newRootValue\"}"); - - final TestVdmComplex siblingGrandchildComplex = - TestVdmComplex.builder().someValue("newSiblingGrandchildValue").build(); - childComplex.setComplexValue(siblingGrandchildComplex); - - final String siblingDeltaPayload = - ODataEntitySerializer.serializeEntityForUpdatePatchRecursiveDelta(rootEntity, additionalFields); - assertThat(siblingDeltaPayload) - .isEqualTo( - "{\"customField\":null,\"ComplexValue\":{\"ComplexValue\":{\"SomeValue\":\"newSiblingGrandchildValue\",\"OtherValue\":null,\"ComplexValue\":null}},\"StringValue\":\"newRootValue\"}"); - } - -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataFilterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataFilterTest.java deleted file mode 100644 index 69c59c59a2..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataFilterTest.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; - -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -class ODataFilterTest -{ - private static final EntityField STRING_FIELD = new EntityField<>("field"); - private static final EntityField ZONED_DATE_TIME_FIELD = new EntityField<>("field"); - private static final EntityField LOCAL_DATE_TIME_FIELD = new EntityField<>("field"); - private static final EntityField LOCAL_TIME_FIELD = new EntityField<>("field"); - private static final EntityField INT_FIELD = new EntityField<>("field"); - - @ParameterizedTest - @EnumSource( TestInput.class ) - void testFilterExpression( @Nonnull final TestInput testInput ) - { - final ValueBoolean clientExpression = testInput.helper.getDelegateExpressionWithoutOuterParentheses(); - assertThat(clientExpression.getExpression(ODataProtocol.V2)).isEqualTo(testInput.expectedFilterString); - - final ODataRequestRead request = - FluentHelperFactory - .withServicePath("/foo/bar") - .read(TestEntity.class, "foo") - .filter(testInput.helper) - .toRequest(); - assertThat(request.getRelativeUri()).hasQuery("$filter=" + testInput.expectedFilterString); - } - - @AllArgsConstructor - enum TestInput - { - STRING_EQUALITY(STRING_FIELD.eq("va'l''ue"), "field eq 'va''l''''ue'"), - STRING_SUBSTRING(STRING_FIELD.substringOf("foo"), "substringof('foo',field)"), - STRING_ENDS_WITH(STRING_FIELD.endsWith("foo"), "endswith(field,'foo')"), - STRING_STARTS_WITH(STRING_FIELD.startsWith("foo"), "startswith(field,'foo')"), - STRING_DISJUNCTION( - STRING_FIELD.startsWith("foo").or(STRING_FIELD.ne("bar")), - "startswith(field,'foo') or (field ne 'bar')"), - STRING_NEGATION(STRING_FIELD.endsWith("foo").not(), "not endswith(field,'foo')"), - STRING_COMPLEX_EXPRESSION( - STRING_FIELD - .substringOf("bar") - .and(STRING_FIELD.endsWith("foo")) - .or(STRING_FIELD.startsWith("foobar").not()) - .not(), - "not ((substringof('bar',field) and endswith(field,'foo')) or (not startswith(field,'foobar')))"), - ZONED_DATE_TIME( - ZONED_DATE_TIME_FIELD - .eq(ZonedDateTime.of(LocalDate.of(2001, 1, 1), LocalTime.of(20, 15), ZoneId.of("UTC"))), - "field eq datetimeoffset'2001-01-01T20:15:00Z'"), - LOCAL_DATE_TIME( - LOCAL_DATE_TIME_FIELD.eq(LocalDateTime.of(LocalDate.of(2001, 1, 1), LocalTime.of(20, 15))), - "field eq datetime'2001-01-01T20:15:00'"), - LOCAL_TIME(LOCAL_TIME_FIELD.eq(LocalTime.of(20, 15)), "field eq time'PT20H15M'"), - INT_LOGICAL_OPERATORS( - INT_FIELD.eq(1).and(ExpressionFluentHelper.not(INT_FIELD.ne(2))).or(INT_FIELD.eq(3)).not(), - "not (((field eq 1) and (not (field ne 2))) or (field eq 3))"), - INT_RELATIVE_OPERATORS( - INT_FIELD.le(1).and(INT_FIELD.ge(2)).and(INT_FIELD.lt(3).and(INT_FIELD.gt(4))), - "((field le 1) and (field ge 2)) and ((field lt 3) and (field gt 4))"), - INT_EQUALITY(INT_FIELD.eq(1), "field eq 1"), - INT_INEQUALITY(INT_FIELD.ne(1), "field ne 1"), - INT_GREATER_THAN(INT_FIELD.gt(1), "field gt 1"), - INT_GREATER_THAN_OR_EQUAL(INT_FIELD.ge(1), "field ge 1"), - INT_LESS_THAN(INT_FIELD.lt(1), "field lt 1"), - INT_LESS_THAN_OR_EQUAL(INT_FIELD.le(1), "field le 1"), - EQUAL_NULL(INT_FIELD.eq(null), "field eq null"), - EQUALNULL(INT_FIELD.eqNull(), "field eq null"), - NOT_EQUAL_NULL(INT_FIELD.ne(null), "field ne null"), - NOT_EQUALNULL(INT_FIELD.neNull(), "field ne null"), - GREATER_THAN_NULL(INT_FIELD.gt(null), "field gt null"), - GREATER_THAN_OR_EQUAL_NULL(INT_FIELD.ge(null), "field ge null"), - LESS_THAN_NULL(INT_FIELD.lt(null), "field lt null"), - LESS_THAN_OR_EQUAL_NULL(INT_FIELD.le(null), "field le null"),; - - @Nonnull - private final ExpressionFluentHelper helper; - @Nonnull - private final String expectedFilterString; - } - - private static class TestEntity extends VdmEntity - { - @Getter - final String entityCollection = null; - - @Getter - private final String defaultServicePath = "/"; - - @Getter - private final Class type = TestEntity.class; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataHeaderTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataHeaderTest.java deleted file mode 100644 index 7b52813c95..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataHeaderTest.java +++ /dev/null @@ -1,244 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.delete; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.patch; -import static com.github.tomakehurst.wiremock.client.WireMock.patchRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.github.tomakehurst.wiremock.matching.UrlPathPattern; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; - -@WireMockTest -class ODataHeaderTest -{ - private static final String DEFAULT_SERVICE_PATH = new TestVdmEntity().getDefaultServicePath(); - private static final String ENTITY_COLLECTION_PATH = new TestVdmEntity().getEntityCollection(); - private static final UrlPathPattern GET_ALL = urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ENTITY_COLLECTION_PATH); - private static final UrlPathPattern CREATE = urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ENTITY_COLLECTION_PATH); - private static final UrlPathPattern UPDATE = - urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ENTITY_COLLECTION_PATH + "(123)"); - private static final UrlPathPattern DELETE = UPDATE; - private static final UrlPathPattern CSRF = urlPathEqualTo(DEFAULT_SERVICE_PATH + "/"); - - private DefaultHttpDestination destination; - private TestVdmEntity entity; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - stubFor(get(GET_ALL).willReturn(okJson("{\"d\":{\"results\":[]}}"))); - stubFor(patch(UPDATE).willReturn(okJson("{\"d\":{}"))); - stubFor(post(CREATE).willReturn(okJson("{\"d\":{}"))); - stubFor(delete(DELETE).willReturn(ok())); - stubFor( - head(CSRF).withHeader("x-csrf-token", equalTo("fetch")).willReturn(ok().withHeader("x-csrf-token", "abc"))); - - entity = TestVdmEntity.builder().integerValue(123).build(); - } - - // Test for implicitly missing CSRF header token and ETag header when GETTING an entity - @Test - void testNonExistingHeadersForGetDefault() - { - new TestEntityReadFluentHelper().executeRequest(destination); - verify(0, headRequestedFor(CSRF)); - verify(getRequestedFor(GET_ALL).withoutHeader("If-Match").withoutHeader("x-csrf-token")); - } - - // Test for CSRF token header and implicitly without ETag header when UPDATING an entity - @Test - void testUpdateDefaultHeader() - { - new TestEntityUpdateFluentHelper(entity).executeRequest(destination); - verify(headRequestedFor(CSRF).withHeader("x-csrf-token", equalTo("fetch"))); - verify(patchRequestedFor(UPDATE).withHeader("x-csrf-token", equalTo("abc")).withoutHeader("If-Match")); - } - - // Test for CSRF token header and explicit ETag header when UPDATING an entity - @Test - void testUpdateSpecificVersionVersionHeader() - { - entity.setVersionIdentifier("ver"); - new TestEntityUpdateFluentHelper(entity).executeRequest(destination); - verify(headRequestedFor(CSRF).withHeader("x-csrf-token", equalTo("fetch"))); - - verify( - patchRequestedFor(UPDATE) - .withHeader("x-csrf-token", equalTo("abc")) - .withHeader("If-Match", equalTo("ver"))); - } - - // Test for CSRF token header and explicit ETag-wildcard header when UPDATING an entity - @Test - void testUpdateWildcardVersionVersionHeader() - { - new TestEntityUpdateFluentHelper(entity).matchAnyVersionIdentifier().executeRequest(destination); - verify(headRequestedFor(CSRF).withHeader("x-csrf-token", equalTo("fetch"))); - - verify( - patchRequestedFor(UPDATE).withHeader("x-csrf-token", equalTo("abc")).withHeader("If-Match", equalTo("*"))); - } - - // Test for CSRF token header, explicitly without ETag header when UPDATING an entity - @Test - void testUpdateWithoutVersionHeader() - { - new TestEntityUpdateFluentHelper(entity).disableVersionIdentifier().executeRequest(destination); - verify(headRequestedFor(CSRF).withHeader("x-csrf-token", equalTo("fetch"))); - verify(patchRequestedFor(UPDATE).withHeader("x-csrf-token", equalTo("abc")).withoutHeader("If-Match")); - } - - // Test for custom header, implicitly without CSRF token header or ETag header when GETTING an entity - @Test - void testGetCustomHeaderHeader() - { - new TestEntityReadFluentHelper() - .withHeader("Authentication", "yes") - .withHeader("Cookie", "tasty") - .executeRequest(destination); - verify(0, headRequestedFor(CSRF)); - - verify( - getRequestedFor(GET_ALL) - .withHeader("Authentication", equalTo("yes")) - .withHeader("Cookie", equalTo("tasty")) - .withoutHeader("x-csrf-token") - .withoutHeader("If-Match")); - } - - // Test for CSRF token header, explicitly with custom headers when UPDATING an entity - @Test - void testUpdateCustomHeader() - { - new TestEntityUpdateFluentHelper(entity).withHeader("Authentication", "yes").executeRequest(destination); - - // Note: with Apache HttpClient 5 the CSRF token HEAD request is issued by the client-level - // CsrfTokenInterceptor, which does not propagate the request's custom headers. - verify(headRequestedFor(CSRF).withHeader("x-csrf-token", equalTo("fetch"))); - - verify( - patchRequestedFor(UPDATE) - .withHeader("Authentication", equalTo("yes")) - .withHeader("x-csrf-token", equalTo("abc"))); - } - - // fluent helpers - - // Test that withoutCsrfToken() disables the CSRF HEAD probe and sends neither a - // CSRF token nor the internal skip marker on the actual write request. - @Test - void testUpdateWithoutCsrfTokenSkipsHead() - { - new TestEntityUpdateFluentHelper(entity).withoutCsrfToken().executeRequest(destination); - - verify(0, headRequestedFor(CSRF)); - verify(patchRequestedFor(UPDATE).withoutHeader("x-csrf-token")); - } - - // Test that the deprecated withCsrfToken() is a no-op on a read: no CSRF HEAD probe is fired. - @SuppressWarnings( "deprecation" ) - @Test - void testReadWithCsrfTokenIsNoOp() - { - new TestEntityReadFluentHelper().withCsrfToken().executeRequest(destination); - - verify(0, headRequestedFor(CSRF)); - verify(getRequestedFor(GET_ALL).withoutHeader("x-csrf-token")); - } - - private static class TestEntityDeleteFluentHelper - extends - FluentHelperDelete - { - private final TestVdmEntity entity; - - @SuppressWarnings( "deprecation" ) - private TestEntityDeleteFluentHelper( @Nonnull final TestVdmEntity entity ) - { - super(DEFAULT_SERVICE_PATH, entity.getEntityCollection()); - this.entity = entity; - } - - @Override - protected TestVdmEntity getEntity() - { - return entity; - } - } - - private static class TestEntityUpdateFluentHelper - extends - FluentHelperUpdate - { - private final TestVdmEntity entity; - - @SuppressWarnings( "deprecation" ) - private TestEntityUpdateFluentHelper( @Nonnull final TestVdmEntity entity ) - { - super(DEFAULT_SERVICE_PATH, entity.getEntityCollection()); - this.entity = entity; - } - - @Override - protected TestVdmEntity getEntity() - { - return entity; - } - } - - private static class TestEntityCreateFluentHelper - extends - FluentHelperCreate - { - private final TestVdmEntity entity; - - @SuppressWarnings( "deprecation" ) - private TestEntityCreateFluentHelper( @Nonnull final TestVdmEntity entity ) - { - super(DEFAULT_SERVICE_PATH, entity.getEntityCollection()); - this.entity = entity; - } - - @Override - protected TestVdmEntity getEntity() - { - return entity; - } - } - - private static class TestEntityReadFluentHelper - extends - FluentHelperRead - { - @SuppressWarnings( "deprecation" ) - private TestEntityReadFluentHelper() - { - super(DEFAULT_SERVICE_PATH, TestVdmEntity.builder().build().getEntityCollection()); - } - - @Nonnull - @Override - protected Class getEntityClass() - { - return TestVdmEntity.class; - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataSelectExpandTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataSelectExpandTest.java deleted file mode 100644 index d81be99c68..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataSelectExpandTest.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; -import java.util.List; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -class ODataSelectExpandTest -{ - @Test - void testSelectExpand() - { - final FluentHelperRead entityRead = - FluentHelperFactory.withServicePath("service/path/").read(TestEntity.class, "TestEntity"); - entityRead.select(TestEntity.FOO, TestEntity.TO_PARENT, TestEntity.TO_CHILDREN.select(TestEntity.BAR)); - - final ODataRequestRead requestRead = entityRead.toRequest(); - - assertThat(requestRead.getQueryString()) - .isEqualTo("$select=Foo,to_Parent/*,to_Children/Bar&$expand=to_Parent,to_Children"); - - assertThat(requestRead.getRelativeUri()) - .hasPath("/service/path/TestEntity") - .hasQuery("$select=Foo,to_Parent/*,to_Children/Bar&$expand=to_Parent,to_Children"); - } - - @Test - void testSelectSome() - { - final FluentHelperRead entityRead = - FluentHelperFactory.withServicePath("service/path/").read(TestEntity.class, "TestEntity"); - entityRead.select(TestEntity.FOO, TestEntity.BAR, TestEntity.FOO); - - final ODataRequestRead requestRead = entityRead.toRequest(); - - assertThat(requestRead.getQueryString()).isEqualTo("$select=Foo,Bar"); - - assertThat(requestRead.getRelativeUri()).hasPath("/service/path/TestEntity").hasQuery("$select=Foo,Bar"); - } - - @Test - void testSelectAll() - { - final FluentHelperRead entityRead = - FluentHelperFactory.withServicePath("service/path/").read(TestEntity.class, "TestEntity"); - entityRead.select(TestEntity.ALL_FIELDS); - - final ODataRequestRead requestRead = entityRead.toRequest(); - - assertThat(requestRead.getQueryString()).isEqualTo("$select=*"); - - assertThat(requestRead.getRelativeUri()).hasPath("/service/path/TestEntity").hasQuery("$select=*"); - } - - @Test - void testExpand() - { - final FluentHelperRead entityRead = - FluentHelperFactory.withServicePath("service/path/").read(TestEntity.class, "TestEntity"); - entityRead.select(TestEntity.TO_PARENT, TestEntity.TO_CHILDREN.select(TestEntity.TO_PARENT)); - - final ODataRequestRead requestRead = entityRead.toRequest(); - - assertThat(requestRead.getQueryString()) - .isEqualTo("$select=to_Parent/*,to_Children/to_Parent/*&$expand=to_Parent,to_Children/to_Parent"); - - assertThat(requestRead.getRelativeUri()) - .hasPath("/service/path/TestEntity") - .hasQuery("$select=to_Parent/*,to_Children/to_Parent/*&$expand=to_Parent,to_Children/to_Parent"); - } - - // full set of generated VDM classes for TestEntity - - @Builder - @Data - @NoArgsConstructor - @AllArgsConstructor - @ToString( doNotUseGetters = true, callSuper = true ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) - public static class TestEntity extends VdmEntity - { - public final static TestEntitySelectable ALL_FIELDS = new TestEntitySelectable() - { - @Getter - public String fieldName = "*"; - @Getter - public List selections = Collections.singletonList("*"); - }; - - public static final TestEntitySelectableOneToOneLink TO_PARENT = - new TestEntitySelectableOneToOneLink<>("to_Parent"); - public static final TestEntitySelectableLink TO_CHILDREN = - new TestEntitySelectableLink<>("to_Children"); - public static final TestEntitySelectableField FOO = new TestEntitySelectableField<>("Foo"); - public static final TestEntitySelectableField BAR = new TestEntitySelectableField<>("Bar"); - - @Getter - final String entityCollection = "TestEntity"; - - @Getter - private final String defaultServicePath = "/"; - - @Getter - private final Class type = TestEntity.class; - - @SerializedName( "to_Parent" ) - @JsonProperty( "to_Parent" ) - @ODataField( odataName = "to_Parent" ) - @Nullable - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private TestEntity toParent; - - @SerializedName( "to_Children" ) - @JsonProperty( "to_Children" ) - @ODataField( odataName = "to_Children" ) - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private List toChildren; - - @SerializedName( "Foo" ) - @JsonProperty( "Foo" ) - @Nullable - @ODataField( odataName = "Foo" ) - private String foo; - - @SerializedName( "Bar" ) - @JsonProperty( "Bar" ) - @Nullable - @ODataField( odataName = "Bar" ) - private String bar; - } - - private interface TestEntitySelectable extends EntitySelectable - { - } - - private static class TestEntitySelectableField extends EntityField - implements - TestEntitySelectable - { - private TestEntitySelectableField( @Nonnull final String fieldName ) - { - super(fieldName); - } - } - - private static class TestEntitySelectableLink> - extends - EntityLink, TestEntity, ObjectT> - implements - TestEntitySelectable - { - private TestEntitySelectableLink( final String fieldName ) - { - super(fieldName); - } - - private TestEntitySelectableLink( - final EntityLink, TestEntity, ObjectT> toClone ) - { - super(toClone); - } - - @Nonnull - @Override - protected TestEntitySelectableLink translateLinkType( - final EntityLink, TestEntity, ObjectT> link ) - { - return new TestEntitySelectableLink(link); - } - } - - private static class TestEntitySelectableOneToOneLink> - extends - TestEntitySelectableLink - implements - OneToOneLink - { - private TestEntitySelectableOneToOneLink( final String fieldName ) - { - super(fieldName); - } - - @Nonnull - @Override - public ExpressionFluentHelper filter( - @Nonnull final ExpressionFluentHelper filterExpression ) - { - return super.filterOnOneToOneLink(filterExpression); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataSerializationTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataSerializationTest.java deleted file mode 100644 index 57c13e6506..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataSerializationTest.java +++ /dev/null @@ -1,342 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.data.MapEntry.entry; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.Month; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -import javax.annotation.Nullable; - -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.io.entity.StringEntity; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.ImmutableMap; -import com.google.gson.Gson; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataDeserializationException; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; -import com.sap.cloud.sdk.s4hana.datamodel.odata.annotation.Key; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -class ODataSerializationTest -{ - // take note that we are NOT using the following values: - // "LocalDateTimeValue": "datetime'1992-01-01T00:00:00'" - // "OffsetDateTimeValue": "datetimeoffset'1992-01-01T00:00:00Z-04:00'" - // "LocalTimeValue": "time'PT13H20M'" - // "GuidValue": "guid'123e4567-e89b-12d3-a456-426614174000'" - private static final String SAMPLE_PAYLOAD = """ - { - "d" : { - "__metadata": { - "uri": "https://services.odata.org/OData/OData.svc/Categories(42)", - "etag": "W/\\"some-version-id\\"", - "type": "DataServiceProviderDemo.Category" - }, - "SByteValue": -127, - "Int16Value": 1337, - "IntegerValue": 42, - "Int64Value": 123456789000, - "DecimalValue": "123456.789", - "DoubleValue": 42.1, - "BooleanValue": true, - "StringValue": "Food", - "LocalDateTimeValue": "/Date(694224000000)/", - "OffsetDateTimeValue": "/Date(694224000000-0240)/", - "LocalTimeValue": "PT13H20M", - "GuidValue": "123e4567-e89b-12d3-a456-426614174000", - "to_Parent": { - "__deferred": { - "uri": "https://services.odata.org/OData/OData.svc/Categories(42)/to_Parent" - } - }, - "to_Children": { - "__deferred": { - "uri": "https://services.odata.org/OData/OData.svc/Categories(42)/to_Children" - } - }, - "UnmappedStringValue": "foo", - "UnmappedArrayValue": ["fizz","buzz"], - "UnmappedComplexValue": {"bar":"fizzbuzz"} - } - } - """; - - @Test - void testBrokenResponse() - { - // setup http response - final ClassicHttpResponse response = mock(ClassicHttpResponse.class); - doReturn(new Header[0]).when(response).getHeaders(); - doReturn(201).when(response).getCode(); - doReturn(new StringEntity("{\"d\":{\"broken\"}}", ContentType.APPLICATION_JSON)).when(response).getEntity(); - - // setup generic request for reference - final ODataRequestGeneric request = mock(ODataRequestGeneric.class); - doReturn(ODataProtocol.V2).when(request).getProtocol(); - - // test entity deserialization - final ODataRequestResultGeneric result = new ODataRequestResultGeneric(request, response); - assertThatCode(() -> result.as(TestEntity.class)).isInstanceOf(ODataDeserializationException.class); - } - - @Test - void testNoValuesResponse() - { - // setup http response - final ClassicHttpResponse response = mock(ClassicHttpResponse.class); - doReturn(new Header[0]).when(response).getHeaders(); - doReturn(201).when(response).getCode(); - doReturn(new StringEntity("{\"d\":{}}", ContentType.APPLICATION_JSON)).when(response).getEntity(); - - // setup generic request for reference - final ODataRequestGeneric request = mock(ODataRequestGeneric.class); - doReturn(ODataProtocol.V2).when(request).getProtocol(); - - // test entity deserialization - final ODataRequestResultGeneric result = new ODataRequestResultGeneric(request, response); - final TestEntity entity = result.as(TestEntity.class); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).isEmpty(); - assertThat(entity.getIntegerValue()).isNull(); - assertThat(entity.getDecimalValue()).isNull(); - assertThat(entity.getDoubleValue()).isNull(); - assertThat(entity.getBooleanValue()).isNull(); - assertThat(entity.getLocalTimeValue()).isNull(); - assertThat(entity.getStringValue()).isNull(); - assertThat(entity.getGuidValue()).isNull(); - assertThat(entity.getLocalDateTimeValue()).isNull(); - assertThat(entity.getOffsetDateTimeValue()).isNull(); - assertThat(entity.getCustomFields()).isEmpty(); - } - - @Test - void testCreateResponse() - { - // setup http response - final ClassicHttpResponse response = mock(ClassicHttpResponse.class); - doReturn(new Header[0]).when(response).getHeaders(); - doReturn(201).when(response).getCode(); - doReturn(new StringEntity(SAMPLE_PAYLOAD, ContentType.APPLICATION_JSON)).when(response).getEntity(); - - // setup generic request for reference - final ODataRequestGeneric request = mock(ODataRequestGeneric.class); - doReturn(ODataProtocol.V2).when(request).getProtocol(); - - // test entity deserialization - final ODataRequestResultGeneric result = new ODataRequestResultGeneric(request, response); - final TestEntity entity = result.as(TestEntity.class); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly("W/\"some-version-id\""); - assertThat(entity.getSByteValue()).isEqualTo((byte) -127); - assertThat(entity.getInt16Value()).isEqualTo((short) 1337); - assertThat(entity.getIntegerValue()).isEqualTo(42); - assertThat(entity.getInt64Value()).isEqualTo(123456789000L); - assertThat(entity.getDecimalValue()).isEqualTo("123456.789"); - assertThat(entity.getDoubleValue()).isEqualTo(42.1); - assertThat(entity.getBooleanValue()).isTrue(); - assertThat(entity.getLocalTimeValue()).isEqualTo(LocalTime.of(13, 20, 0)); - assertThat(entity.getStringValue()).isEqualTo("Food"); - assertThat(entity.getGuidValue()).isEqualTo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000")); - assertThat(entity.getLocalDateTimeValue()).isEqualTo(LocalDate.of(1992, Month.JANUARY, 1).atStartOfDay()); - assertThat(entity.getOffsetDateTimeValue()) - .isEqualTo( - LocalDate - .of(1992, Month.JANUARY, 1) - .atStartOfDay() - .atZone(ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4)))); - - assertThat(entity.getCustomFields()) - .containsExactly( - entry("UnmappedStringValue", "foo"), - entry("UnmappedArrayValue", Arrays.asList("fizz", "buzz")), - entry("UnmappedComplexValue", ImmutableMap.of("bar", "fizzbuzz"))); - } - - @Test - void testSerialisationForDateTimeAttributes() - { - final String SERIALIZED_ENTITY = """ - {\ - "IntegerValue":1,\ - "OffsetDateTimeValue":"/Date(694224000000-0240)/",\ - "LocalTimeValue":"PT13H20M0S",\ - "LocalDateTimeValue":"/Date(694224000000)/"\ - }\ - """; - final TestEntity entity = new TestEntity(); - - entity.setIntegerValue(1); - entity.setLocalDateTimeValue(LocalDate.of(1992, Month.JANUARY, 1).atStartOfDay()); - entity.setLocalTimeValue(LocalTime.of(13, 20, 0)); - entity - .setOffsetDateTimeValue( - LocalDate - .of(1992, Month.JANUARY, 1) - .atStartOfDay() - .atZone(ZoneId.ofOffset("GMT", ZoneOffset.ofHours(-4)))); - - final String serialisedEntity = new Gson().toJson(entity); - - assertThat(serialisedEntity).isEqualTo(SERIALIZED_ENTITY); - } - - @Builder - @Data - @NoArgsConstructor - @AllArgsConstructor - @ToString( doNotUseGetters = true, callSuper = true ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) - public static class TestEntity extends VdmEntity - { - @Getter - final String entityCollection = null; - - @Getter - private final String defaultServicePath = "/"; - - @Getter - private final Class type = TestEntity.class; - - @Key - @SerializedName( "IntegerValue" ) - @JsonProperty( "IntegerValue" ) - @Nullable - @ODataField( odataName = "IntegerValue" ) - private Integer integerValue; - - @SerializedName( "GuidValue" ) - @JsonProperty( "GuidValue" ) - @Nullable - @ODataField( odataName = "GuidValue" ) - private UUID guidValue; - - @SerializedName( "StringValue" ) - @JsonProperty( "StringValue" ) - @Nullable - @ODataField( odataName = "StringValue" ) - private String stringValue; - - @SerializedName( "OffsetDateTimeValue" ) - @JsonProperty( "OffsetDateTimeValue" ) - @Nullable - @JsonSerialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonZonedDateTimeSerializer.class ) - @JsonDeserialize( - using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonZonedDateTimeDeserializer.class ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ZonedDateTimeAdapter.class ) - @ODataField( - odataName = "OffsetDateTimeValue", - converter = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ZonedDateTimeCalendarConverter.class ) - private ZonedDateTime offsetDateTimeValue; - - @SerializedName( "to_Parent" ) - @JsonProperty( "to_Parent" ) - @ODataField( odataName = "to_Parent" ) - @Nullable - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private TestEntity toParent; - - @SerializedName( "to_Children" ) - @JsonProperty( "to_Children" ) - @ODataField( odataName = "to_Children" ) - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private List toChildren; - - @SerializedName( "DecimalValue" ) - @JsonProperty( "DecimalValue" ) - @Nullable - @ODataField( odataName = "DecimalValue" ) - private BigDecimal decimalValue; - - @SerializedName( "DoubleValue" ) - @JsonProperty( "DoubleValue" ) - @Nullable - @ODataField( odataName = "DoubleValue" ) - private Double doubleValue; - - @SerializedName( "LocalTimeValue" ) - @JsonProperty( "LocalTimeValue" ) - @Nullable - @JsonSerialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonLocalTimeSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonLocalTimeDeserializer.class ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalTimeAdapter.class ) - @ODataField( - odataName = "LocalTimeValue", - converter = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalTimeCalendarConverter.class ) - private LocalTime localTimeValue; - - @SerializedName( "LocalDateTimeValue" ) - @JsonProperty( "LocalDateTimeValue" ) - @Nullable - @JsonSerialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonLocalDateTimeSerializer.class ) - @JsonDeserialize( - using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonLocalDateTimeDeserializer.class ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalDateTimeAdapter.class ) - @ODataField( - odataName = "LocalDateTimeValue", - converter = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalDateTimeCalendarConverter.class ) - private LocalDateTime localDateTimeValue; - - @SerializedName( "BooleanValue" ) - @JsonProperty( "BooleanValue" ) - @Nullable - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataBooleanAdapter.class ) - @ODataField( odataName = "BooleanValue" ) - private Boolean booleanValue; - - @SerializedName( "SByteValue" ) - @JsonProperty( "SByteValue" ) - @Nullable - @ODataField( odataName = "SByteValue" ) - private Byte sByteValue; - - @SerializedName( "Int16Value" ) - @JsonProperty( "Int16Value" ) - @Nullable - @ODataField( odataName = "Int16Value" ) - private Short int16Value; - - @SerializedName( "Int64Value" ) - @JsonProperty( "Int64Value" ) - @Nullable - @ODataField( odataName = "Int64Value" ) - private Long int64Value; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataV2FunctionImportBatchIntegrationTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataV2FunctionImportBatchIntegrationTest.java deleted file mode 100644 index 8c9960d45e..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataV2FunctionImportBatchIntegrationTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.containing; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; - -import java.time.LocalDateTime; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.helper.batch.BatchChangeSetFluentHelperBasic; -import com.sap.cloud.sdk.datamodel.odata.helper.batch.BatchFluentHelperBasic; - -import lombok.Getter; - -@WireMockTest -class ODataV2FunctionImportBatchIntegrationTest -{ - private static final String ODATA_ENDPOINT_URL = "/path/to/service"; - private static final String ODATA_ENDPOINT_BATCH_URL = ODATA_ENDPOINT_URL + "/$batch"; - - @Getter - public static class TestBatch extends BatchFluentHelperBasic - { - private final String servicePathForBatchRequest = ODATA_ENDPOINT_URL; - - @Nonnull - @Override - protected TestBatch getThis() - { - return this; - } - - @Nonnull - @Override - public TestBatchChangeset beginChangeSet() - { - return new TestBatchChangeset(this); - } - } - - public static class TestBatchChangeset extends BatchChangeSetFluentHelperBasic - { - public TestBatchChangeset( final TestBatch parent ) - { - super(parent, parent); - } - - @Nonnull - @Override - protected TestBatchChangeset getThis() - { - return this; - } - } - - @Test - void testFunctionImportWithPostInChangeSet( @Nonnull final WireMockRuntimeInfo wm ) - { - stubFor(head(urlEqualTo(ODATA_ENDPOINT_URL)).willReturn(noContent())); - stubFor(post(urlEqualTo(ODATA_ENDPOINT_BATCH_URL)).willReturn(ok())); - - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - final Map parameters = new LinkedHashMap<>(); - parameters.put("FileDocumentYear", "2015"); - parameters.put("FileDocument", "00281"); - parameters.put("FileDocumentItem", "1"); - parameters.put("PostingDate", LocalDateTime.of(2015, 1, 12, 12, 12)); - - final FluentHelperFunction functionImport = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .functionSinglePost(parameters, "CancelItem", Void.class); - - new TestBatch().beginChangeSet().addFunctionImport(functionImport).endChangeSet().executeRequest(destination); - - final String functionImportPost = - "POST CancelItem?FileDocumentYear='2015'&FileDocument='00281'&FileDocumentItem='1'&PostingDate=datetime'2015-01-12T12:12:00' HTTP/1.1"; - - verify(postRequestedFor(urlEqualTo(ODATA_ENDPOINT_BATCH_URL)).withRequestBody(containing(functionImportPost))); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataV2FunctionImportGetBatchIntegrationTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataV2FunctionImportGetBatchIntegrationTest.java deleted file mode 100644 index cda983383e..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataV2FunctionImportGetBatchIntegrationTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.containing; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; - -import java.util.Collections; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.helper.batch.BatchFluentHelperBasic; -import com.sap.cloud.sdk.datamodel.odata.helper.batch.FluentHelperBatchEndChangeSet; - -@WireMockTest -class ODataV2FunctionImportGetBatchIntegrationTest -{ - private static final String ODATA_ENDPOINT_URL = "/path/to/service"; - private static final String ODATA_ENDPOINT_BATCH_URL = ODATA_ENDPOINT_URL + "/$batch"; - - public static class TestBatch extends BatchFluentHelperBasic - implements - FluentHelperBatchEndChangeSet - { - @Nonnull - @Override - protected String getServicePathForBatchRequest() - { - return ODATA_ENDPOINT_URL; - } - - @Nonnull - @Override - protected TestBatch getThis() - { - return this; - } - - @Nonnull - @Override - public TestBatch beginChangeSet() - { - return this; - } - - @Nonnull - @Override - public TestBatch endChangeSet() - { - return this; - } - } - - @Test - void testFunctionImportWithGetInReadOperation( @Nonnull final WireMockRuntimeInfo wm ) - { - stubFor(head(urlEqualTo(ODATA_ENDPOINT_URL)).willReturn(noContent())); - stubFor(post(urlEqualTo(ODATA_ENDPOINT_BATCH_URL)).willReturn(ok())); - - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - final FluentHelperFunction functionImport = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .functionSingleGet(Collections.emptyMap(), "Get_TestQueue", String.class); - - new TestBatch().addReadOperations(functionImport).executeRequest(destination); - - final String functionImportGet = "GET Get_TestQueue HTTP/1.1"; - - verify(postRequestedFor(urlEqualTo(ODATA_ENDPOINT_BATCH_URL)).withRequestBody(containing(functionImportGet))); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataV2FunctionImportIntegrationTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataV2FunctionImportIntegrationTest.java deleted file mode 100644 index ec846cf235..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ODataV2FunctionImportIntegrationTest.java +++ /dev/null @@ -1,232 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.equalToIgnoreCase; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import java.time.LocalDateTime; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.core5.http.HttpStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; -import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -@WireMockTest -class ODataV2FunctionImportIntegrationTest -{ - private static final String ODATA_ENDPOINT_URL = "/path/to/service"; - private static final String ODATA_FUNCTION_IMPORT_URL = ODATA_ENDPOINT_URL + "/CancelItem.*"; - private static final String CSRF_TOKEN = "awesome-token"; - private static final String CSRF_TOKEN_HEADER_KEY = "x-csrf-token"; - - private static final String XML_ERROR_STRING = "an exception was raised ";; - private static final String JSON_ERROR_STRING = "{\"error\": \"an exception was raised\"}"; - private static final String RESPONSE = """ - {"d": { - "CancelItem": { - "__metadata": { "type": "API_TEST_SRV/CancelItem" }, - "SomeField": "1010" - } - }} - """; - - @Data - @EqualsAndHashCode( callSuper = true ) - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class TestEntity extends VdmEntity - { - private final String entityCollection = "TestEntities"; - private final Class type = TestEntity.class; - - @SerializedName( "SomeField" ) - @JsonProperty( "SomeField" ) - @ODataField( odataName = "SomeField" ) - private String someField; - } - - private DefaultHttpDestination destination; - - @BeforeEach - void before( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - } - - @Test - void testFunctionImportWithCsrfToken() - { - wiremockIssueCsrfTokenWithStatusCodeOk(); - - wiremockSendSuccesfulJsonResponse(); - - final TestEntity materialDocumentItem = getCancelItemFluentHelper().executeRequest(destination); - - assertOnFunctionImportResponseEntity(materialDocumentItem); - } - - @Test - void testFunctionImportWithCsrfTokenAndServerErrorResponse() - { - wiremockIssueCsrfToken(WireMock.serverError()); - - wiremockSendSuccesfulJsonResponse(); - - final TestEntity materialDocumentItem = getCancelItemFluentHelper().executeRequest(destination); - - assertOnFunctionImportResponseEntity(materialDocumentItem); - } - - @Test - void testFunctionImportWithCsrfTokenAndMethodNotAllowedResponse() - { - wiremockIssueCsrfToken(WireMock.status(HttpStatus.SC_METHOD_NOT_ALLOWED)); - - wiremockSendSuccesfulJsonResponse(); - - final TestEntity materialDocumentItem = getCancelItemFluentHelper().executeRequest(destination); - - assertOnFunctionImportResponseEntity(materialDocumentItem); - } - - @Test - void testFunctionImportWithoutCsrfToken() - { - //Return OK but without csrf token in response header - stubFor( - head(urlEqualTo(ODATA_ENDPOINT_URL + "/")) - .withHeader(CSRF_TOKEN_HEADER_KEY, equalToIgnoreCase("Fetch")) - .willReturn(WireMock.ok())); - - stubFor(post(urlMatching(ODATA_FUNCTION_IMPORT_URL)).willReturn(okJson(RESPONSE))); - - final TestEntity materialDocumentItem = getCancelItemFluentHelper().executeRequest(destination); - - assertOnFunctionImportResponseEntity(materialDocumentItem); - - verify(postRequestedFor(urlMatching(ODATA_FUNCTION_IMPORT_URL)).withoutHeader(CSRF_TOKEN_HEADER_KEY)); - } - - @Test - void testFunctionImportWithJsonError() - { - wiremockIssueCsrfTokenWithStatusCodeOk(); - - stubFor( - post(urlMatching(ODATA_FUNCTION_IMPORT_URL)).willReturn(WireMock.badRequest().withBody(JSON_ERROR_STRING))); - - assertThatExceptionOfType(ODataException.class) - .isThrownBy(() -> getCancelItemFluentHelper().executeRequest(destination)); - } - - @Test - void testFunctionImportReturnsSanitizedXmlErrorBody() - { - wiremockIssueCsrfTokenWithStatusCodeOk(); - - stubFor( - post(urlMatching(ODATA_FUNCTION_IMPORT_URL)).willReturn(WireMock.badRequest().withBody(XML_ERROR_STRING))); - - assertThatExceptionOfType(ODataException.class) - .isThrownBy(() -> getCancelItemFluentHelper().executeRequest(destination)); - } - - /* - * This test verifies that header manipulation attempts (sneaking in new line characters in "user-supplied" HTTP - * header) are prevented. At this place this should not be necessary, as the system we receive the CSRF token from - * is the same one receiving the next request with this token, so any manipulation would hit the system that sent us - * the manipulated csrf token in the first place. But, better be safe than sorry, so we remove all non-printable - * characters. - * Additional note: As tomcat (so also wiremock) automatically removes new line characters we cannot directly test - * this logic here, therefore we test it with a tab (\t) character. - */ - @Test - void testNonPrintableCharactersRemovedFromCsrfTokenValue() - { - final String tokenWithNonPrintableCharacters = CSRF_TOKEN + "\t%0a%0d"; - final String tokenWithoutNonPrintableCharacters = CSRF_TOKEN + "%0a%0d"; - - stubFor( - head(urlEqualTo(ODATA_ENDPOINT_URL + "/")) - .withHeader(CSRF_TOKEN_HEADER_KEY, equalToIgnoreCase("Fetch")) - .willReturn(ok().withHeader(CSRF_TOKEN_HEADER_KEY, tokenWithNonPrintableCharacters))); - - stubFor( - post(urlMatching(ODATA_FUNCTION_IMPORT_URL)) - .withHeader(CSRF_TOKEN_HEADER_KEY, equalTo(tokenWithoutNonPrintableCharacters)) - .willReturn(okJson(RESPONSE))); - - getCancelItemFluentHelper().executeRequest(destination); - - verify( - postRequestedFor(urlMatching(ODATA_FUNCTION_IMPORT_URL)) - .withHeader(CSRF_TOKEN_HEADER_KEY, equalTo(tokenWithoutNonPrintableCharacters))); - } - - private void wiremockIssueCsrfToken( final ResponseDefinitionBuilder responseDefinitionBuilder ) - { - stubFor( - head(urlEqualTo(ODATA_ENDPOINT_URL + "/")) - .withHeader(CSRF_TOKEN_HEADER_KEY, equalToIgnoreCase("Fetch")) - .willReturn(responseDefinitionBuilder.withHeader(CSRF_TOKEN_HEADER_KEY, CSRF_TOKEN))); - } - - private void wiremockIssueCsrfTokenWithStatusCodeOk() - { - wiremockIssueCsrfToken(WireMock.ok()); - } - - private void assertOnFunctionImportResponseEntity( @Nullable final TestEntity materialDocumentItem ) - { - assertThat(materialDocumentItem).isNotNull(); - assertThat(materialDocumentItem.getSomeField()).isEqualTo("1010"); - } - - private void wiremockSendSuccesfulJsonResponse() - { - stubFor( - post(urlMatching(ODATA_FUNCTION_IMPORT_URL)) - .withHeader(CSRF_TOKEN_HEADER_KEY, equalTo(CSRF_TOKEN)) - .willReturn(okJson(RESPONSE))); - } - - private static FluentHelperFunction getCancelItemFluentHelper() - { - final Map parameters = new LinkedHashMap<>(); - parameters.put("FileDocumentYear", "2015"); - parameters.put("FileDocument", "00281"); - parameters.put("FileDocumentItem", "1"); - parameters.put("PostingDate", LocalDateTime.of(2015, 1, 12, 12, 12)); - return FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .functionSinglePost(parameters, "CancelItem", TestEntity.class); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/PaginationIntegrationTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/PaginationIntegrationTest.java deleted file mode 100644 index f3f29fc6d8..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/PaginationIntegrationTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.sap.cloud.sdk.datamodel.odata.helper.PaginationUnitTest.Customer; -import static com.sap.cloud.sdk.datamodel.odata.helper.PaginationUnitTest.newCustomerRead; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import java.net.URI; -import java.util.List; -import java.util.Objects; -import java.util.stream.Stream; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatcher; - -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; - -@Disabled( "Test runs against a v4 reference service on odata.org. Use it only to manually verify behaviour." ) -class PaginationIntegrationTest -{ - private static final int PAGE_SIZE = 20; - private static final ArgumentMatcher URI_WITHOUT_SKIP_TOKEN = uri -> !uri.getQuery().contains("$skiptoken"); - private static final ArgumentMatcher URL_WITH_SKIP_TOKEN = uri -> uri.getQuery().contains("$skiptoken"); - - private final HttpDestination destination = DefaultHttpDestination.builder("https://services.odata.org").build(); - - @Test - void testGetAll() - { - final HttpDestination destination = spy(this.destination); - - final List result = - newCustomerRead().select(Customer.CUSTOMER_ID).withPreferredPageSize(PAGE_SIZE).executeRequest(destination); - - verify(destination, times(1)).getHeaders(argThat(URI_WITHOUT_SKIP_TOKEN)); - verify(destination, atLeastOnce()).getHeaders(argThat(URL_WITH_SKIP_TOKEN)); - - assertThat(result) - .isNotEmpty() - .extracting(PaginationUnitTest.Customer::getCustomerId) - .allMatch(Objects::nonNull); - } - - @Test - void testGetAllIteratingEntities() - { - final HttpDestination destination = spy(this.destination); - - final Iterable result = - newCustomerRead() - .select(Customer.CUSTOMER_ID) - .withPreferredPageSize(20) - .iteratingEntities() - .executeRequest(destination); - - verify(destination, times(1)).getHeaders(argThat(URI_WITHOUT_SKIP_TOKEN)); - verify(destination, never()).getHeaders(argThat(URL_WITH_SKIP_TOKEN)); - reset(destination); - - int countEntities = 0; - for( final Customer entity : result ) { - countEntities++; - assertThat(entity).extracting(Customer::getCustomerId).isNotNull(); - } - - verify(destination, never()).getHeaders(argThat(URI_WITHOUT_SKIP_TOKEN)); - verify(destination, atLeastOnce()).getHeaders(argThat(URL_WITH_SKIP_TOKEN)); - assertThat(countEntities).isGreaterThan(0); - } - - @Test - void testGetAllStreamingEntities() - { - final HttpDestination destination = spy(this.destination); - - final Stream result = - newCustomerRead() - .select(Customer.CUSTOMER_ID) - .withPreferredPageSize(20) - .streamingEntities() - .executeRequest(destination); - - verify(destination, times(1)).getHeaders(argThat(URI_WITHOUT_SKIP_TOKEN)); - verify(destination, never()).getHeaders(argThat(URL_WITH_SKIP_TOKEN)); - reset(destination); - - final Stream intermediateStream = result.map(Customer::getCustomerId).peek(Objects::requireNonNull); - verify(destination, never()).getHeaders(any()); - - final long countEntities = intermediateStream.count(); - assertThat(countEntities).isGreaterThan(0); - verify(destination, never()).getHeaders(argThat(URI_WITHOUT_SKIP_TOKEN)); - verify(destination, atLeastOnce()).getHeaders(argThat(URL_WITH_SKIP_TOKEN)); - } - - @Test - void testGetAllIteratingPages() - { - final HttpDestination destination = spy(this.destination); - - final Iterable> result = - newCustomerRead() - .select(Customer.CUSTOMER_ID) - .withPreferredPageSize(PAGE_SIZE) - .iteratingPages() - .executeRequest(destination); - - verify(destination, times(1)).getHeaders(argThat(URI_WITHOUT_SKIP_TOKEN)); - verify(destination, never()).getHeaders(argThat(URL_WITH_SKIP_TOKEN)); - reset(destination); - - int countPages = 0; - int countEntities = 0; - for( final List entities : result ) { - countPages++; - countEntities += entities.size(); - assertThat(entities).extracting(Customer::getCustomerId).allMatch(Objects::nonNull); - } - - verify(destination, never()).getHeaders(argThat(URI_WITHOUT_SKIP_TOKEN)); - verify(destination, times(countPages - 1)).getHeaders(argThat(URL_WITH_SKIP_TOKEN)); - assertThat(countEntities).isGreaterThan(0); - assertThat(countPages).isGreaterThan(1); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/PaginationUnitTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/PaginationUnitTest.java deleted file mode 100644 index 181a72f2fa..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/PaginationUnitTest.java +++ /dev/null @@ -1,232 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.absent; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Stream; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.github.tomakehurst.wiremock.matching.UrlPattern; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; -import com.sap.cloud.sdk.s4hana.datamodel.odata.annotation.Key; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@WireMockTest -class PaginationUnitTest -{ - private static final int PAGE_SIZE = 20; - private static final int ENTITIES_COUNT = 91; - private static final int PAGES_COUNT = 5; - - private DefaultHttpDestination destination; - - // corresponds to https://services.odata.org/V4/Northwind/Northwind.svc/Customers?$select=CustomerID - final String page1 = - "{ \"d\" : { \"results\": [ { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('ALFKI')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"ALFKI\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('ANATR')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"ANATR\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('ANTON')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"ANTON\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('AROUT')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"AROUT\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('BERGS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"BERGS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('BLAUS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"BLAUS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('BLONP')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"BLONP\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('BOLID')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"BOLID\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('BONAP')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"BONAP\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('BOTTM')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"BOTTM\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('BSBEV')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"BSBEV\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('CACTU')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"CACTU\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('CENTC')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"CENTC\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('CHOPS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"CHOPS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('COMMI')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"COMMI\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('CONSH')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"CONSH\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('DRACD')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"DRACD\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('DUMON')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"DUMON\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('EASTC')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"EASTC\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('ERNSH')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"ERNSH\" } ], \"__next\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers?$select=CustomerID&$skiptoken='ERNSH'\" } }"; - final String page2 = - "{ \"d\" : { \"results\": [ { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('FAMIA')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"FAMIA\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('FISSA')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"FISSA\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('FOLIG')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"FOLIG\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('FOLKO')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"FOLKO\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('FRANK')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"FRANK\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('FRANR')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"FRANR\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('FRANS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"FRANS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('FURIB')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"FURIB\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('GALED')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"GALED\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('GODOS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"GODOS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('GOURL')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"GOURL\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('GREAL')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"GREAL\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('GROSR')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"GROSR\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('HANAR')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"HANAR\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('HILAA')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"HILAA\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('HUNGC')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"HUNGC\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('HUNGO')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"HUNGO\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('ISLAT')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"ISLAT\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('KOENE')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"KOENE\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('LACOR')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"LACOR\" } ], \"__next\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers?$select=CustomerID&$skiptoken='LACOR'\" } }"; - final String page3 = - "{ \"d\" : { \"results\": [ { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('LAMAI')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"LAMAI\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('LAUGB')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"LAUGB\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('LAZYK')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"LAZYK\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('LEHMS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"LEHMS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('LETSS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"LETSS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('LILAS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"LILAS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('LINOD')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"LINOD\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('LONEP')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"LONEP\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('MAGAA')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"MAGAA\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('MAISD')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"MAISD\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('MEREP')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"MEREP\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('MORGK')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"MORGK\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('NORTS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"NORTS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('OCEAN')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"OCEAN\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('OLDWO')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"OLDWO\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('OTTIK')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"OTTIK\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('PARIS')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"PARIS\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('PERIC')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"PERIC\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('PICCO')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"PICCO\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('PRINI')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"PRINI\" } ], \"__next\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers?$select=CustomerID&$skiptoken='PRINI'\" } }"; - final String page4 = - "{ \"d\" : { \"results\": [ { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('QUEDE')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"QUEDE\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('QUEEN')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"QUEEN\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('QUICK')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"QUICK\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('RANCH')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"RANCH\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('RATTC')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"RATTC\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('REGGC')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"REGGC\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('RICAR')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"RICAR\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('RICSU')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"RICSU\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('ROMEY')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"ROMEY\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('SANTG')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"SANTG\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('SAVEA')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"SAVEA\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('SEVES')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"SEVES\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('SIMOB')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"SIMOB\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('SPECD')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"SPECD\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('SPLIR')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"SPLIR\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('SUPRD')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"SUPRD\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('THEBI')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"THEBI\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('THECR')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"THECR\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('TOMSP')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"TOMSP\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('TORTU')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"TORTU\" } ], \"__next\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers?$select=CustomerID&$skiptoken='TORTU'\" } }"; - final String page5 = - "{ \"d\" : { \"results\": [ { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('TRADH')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"TRADH\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('TRAIH')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"TRAIH\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('VAFFE')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"VAFFE\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('VICTE')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"VICTE\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('VINET')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"VINET\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('WANDK')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"WANDK\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('WARTH')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"WARTH\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('WELLI')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"WELLI\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('WHITC')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"WHITC\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('WILMK')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"WILMK\" }, { \"__metadata\": { \"uri\": \"https://services.odata.org/V2/Northwind/Northwind.svc/Customers('WOLZA')\", \"type\": \"NorthwindModel.Customer\" }, \"CustomerID\": \"WOLZA\" } ] } }"; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - stubFor( - get(UrlPattern.ANY) - .withHeader("Prefer", equalTo("odata.maxpagesize=20")) - .withQueryParam("$skiptoken", absent()) - .willReturn(okJson(page1))); - stubFor( - get(UrlPattern.ANY) - .withHeader("Prefer", equalTo("odata.maxpagesize=20")) - .withQueryParam("$skiptoken", equalTo("'ERNSH'")) - .willReturn(okJson(page2))); - stubFor( - get(UrlPattern.ANY) - .withHeader("Prefer", equalTo("odata.maxpagesize=20")) - .withQueryParam("$skiptoken", equalTo("'LACOR'")) - .willReturn(okJson(page3))); - stubFor( - get(UrlPattern.ANY) - .withHeader("Prefer", equalTo("odata.maxpagesize=20")) - .withQueryParam("$skiptoken", equalTo("'PRINI'")) - .willReturn(okJson(page4))); - stubFor( - get(UrlPattern.ANY) - .withHeader("Prefer", equalTo("odata.maxpagesize=20")) - .withQueryParam("$skiptoken", equalTo("'TORTU'")) - .willReturn(okJson(page5))); - - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - } - - @Test - void testGetAll() - { - final List result = - newCustomerRead().select(Customer.CUSTOMER_ID).withPreferredPageSize(PAGE_SIZE).executeRequest(destination); - - verify(PAGES_COUNT, getRequestedFor(UrlPattern.ANY)); - - Assertions - .assertThat(result) - .hasSize(ENTITIES_COUNT) - .extracting(Customer::getCustomerId) - .allMatch(Objects::nonNull); - } - - @Test - void testGetAllIteratingEntities() - { - final Iterable result = - newCustomerRead() - .select(Customer.CUSTOMER_ID) - .withPreferredPageSize(PAGE_SIZE) - .iteratingEntities() - .executeRequest(destination); - - verify(1, getRequestedFor(UrlPattern.ANY)); - - int countPages = 0; - int countEntities = 0; - for( final Customer entity : result ) { - if( countEntities++ % PAGE_SIZE == 0 ) { - verify(++countPages, getRequestedFor(UrlPattern.ANY)); - } - assertThat(entity).extracting(Customer::getCustomerId).isNotNull(); - } - - assertThat(countEntities).isEqualTo(ENTITIES_COUNT); - verify(PAGES_COUNT, getRequestedFor(UrlPattern.ANY)); - } - - @Test - void testGetAllStreamingEntities() - { - final Stream result = - newCustomerRead() - .select(Customer.CUSTOMER_ID) - .withPreferredPageSize(PAGE_SIZE) - .streamingEntities() - .executeRequest(destination); - - verify(1, getRequestedFor(UrlPattern.ANY)); - - final Stream intermediateStream = result.map(Customer::getCustomerId).peek(Objects::requireNonNull); - verify(1, getRequestedFor(UrlPattern.ANY)); - - final long countEntities = intermediateStream.count(); - assertThat(countEntities).isEqualTo(ENTITIES_COUNT); - verify(PAGES_COUNT, getRequestedFor(UrlPattern.ANY)); - - // repeated access to stream is not permitted as of Java API - assertThatIllegalStateException() - .isThrownBy(result::count) - .withMessage("stream has already been operated upon or closed"); - } - - @Test - void testGetAllIteratingPages() - { - final Iterable> result = - newCustomerRead() - .select(Customer.CUSTOMER_ID) - .withPreferredPageSize(PAGE_SIZE) - .iteratingPages() - .executeRequest(destination); - - verify(1, getRequestedFor(UrlPattern.ANY)); - - int countPages = 0; - int countEntities = 0; - for( final List entities : result ) { - verify(++countPages, getRequestedFor(UrlPattern.ANY)); - countEntities += entities.size(); - Assertions.assertThat(entities).extracting(Customer::getCustomerId).allMatch(Objects::nonNull); - } - assertThat(countEntities).isEqualTo(ENTITIES_COUNT); - } - - @Builder - @Data - @NoArgsConstructor - @AllArgsConstructor - @ToString( doNotUseGetters = true, callSuper = true ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) - public static class Customer extends VdmEntity - { - private static final String SERVICE_PATH = "/V2/Northwind/Northwind.svc"; - - @Getter - final String entityCollection = "Customers"; - - @Getter - private final String defaultServicePath = SERVICE_PATH; - - @Getter - private final Class type = - com.sap.cloud.sdk.datamodel.odata.helper.TestVdmEntity.class; - - @Key - @SerializedName( "CustomerID" ) - @JsonProperty( "CustomerID" ) - @Nullable - @ODataField( odataName = "CustomerID" ) - private String customerId; - - public final static CustomerField CUSTOMER_ID = new CustomerField("CustomerID"); - } - - public static class CustomerField extends EntityField implements CustomerSelectable - { - public CustomerField( final String fieldName ) - { - super(fieldName); - } - } - - public interface CustomerSelectable extends EntitySelectable - { - } - - static < - T extends FluentHelperRead> - FluentHelperRead - newCustomerRead() - { - return FluentHelperFactory.withServicePath(Customer.SERVICE_PATH).read(Customer.class, "Customers"); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/SerializerRaceConditionTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/SerializerRaceConditionTest.java deleted file mode 100644 index c3cdcac038..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/SerializerRaceConditionTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import org.junit.jupiter.api.Test; - -import lombok.SneakyThrows; - -class SerializerRaceConditionTest -{ - @SneakyThrows - @Test - void testRaceCondition() - { - final TestVdmEntity entity = new TestVdmEntity(); - entity.setStringValue("string"); - entity.setDoubleValue(13.37); - entity.setBooleanValue(true); - entity.setIntegerValue(42); - entity.setDecimalValue(BigDecimal.ONE); - entity.setLocalDateTimeValue(LocalDateTime.of(2000, 1, 1, 0, 0)); - entity.setOffsetDateTimeValue(ZonedDateTime.of(2000, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)); - - final String EXPECTED_JSON = - "{\"IntegerValue\":42,\"StringValue\":\"string\",\"OffsetDateTimeValue\":\"/Date(946684800000)/\",\"DecimalValue\":\"1\",\"DoubleValue\":\"13.37\",\"LocalDateTimeValue\":\"/Date(946684800000)/\",\"BooleanValue\":true}"; - - final Collection> tasks = - Collections.nCopies(10_000, () -> ODataEntitySerializer.serializeEntityForCreate(entity)); - - final ExecutorService executor = Executors.newCachedThreadPool(); - for( final Future future : executor.invokeAll(tasks) ) { - assertThat(future.get()).isEqualTo(EXPECTED_JSON); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ServicePathTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ServicePathTest.java deleted file mode 100644 index f53cb2406d..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/ServicePathTest.java +++ /dev/null @@ -1,433 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static java.lang.String.format; - -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.patch; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.common.collect.ImmutableMap; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -/* - * SERVICE A: - * GET /FunctionToMedia(Param1,Param2) -> multiple entities from A_MediaType - * GET /A_MediaEntitySet(Id)/$value -> media lookup - * - * SERVICE B: - * GET /A_EntitySet -> multiple entities from A_EntityType - * GET /A_EntitySet(Id) -> single entity from A_EntityType - * GET /A_EntitySet(Id)/to_RelatedEntity -> single entity from A_RelatedType - * GET /A_EntitySet(Id)/to_RelatedEntity/to_TransitiveEntity -> multiple entities from A_TransitiveType - * GET /A_RelatedEntitySet(Id) -> single entity from A_RelatedType - * GET /A_RelatedEntitySet(Id)/to_TransitiveEntity -> multiple entities from A_TransitiveType - */ -@WireMockTest -class ServicePathTest -{ - private static final String SERVICE_A = "/path/to/serviceA"; - private static final String SERVICE_B = "/path/to/serviceB"; - - private HttpDestination destination; - - @BeforeEach - void setupDestination( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - stubFor(head(urlEqualTo(SERVICE_A)).willReturn(ok())); - stubFor(head(urlEqualTo(SERVICE_B)).willReturn(ok())); - } - - @Test - void testChildEntityFromGet() - { - final String responseEntityListWithRelated = - createPayloadMultiple(createTestEntity("72", createRelatedEntity("13857", null))); - stubFor(get(urlPathMatching(SERVICE_B + "/A_EntitySet")).willReturn(okJson(responseEntityListWithRelated))); - - final String responseEntityListTransitive = createPayloadMultiple(createTransitiveEntity("Bar")); - stubFor( - get(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity")) - .willReturn(okJson(responseEntityListTransitive))); - - final List testEntitys = - FluentHelperFactory - .withServicePath(SERVICE_B) - .read(TestEntity.class, "A_EntitySet") - .select(TestEntity.TO_RELATED_ENTITY) - .executeRequest(destination); - - final RelatedEntity bpRelatedEntity = testEntitys.get(0).getRelatedEntity(); - assertThat(bpRelatedEntity).isNotNull(); - - final List bpTransitiveEntity = bpRelatedEntity.fetchTransitiveEntity(); - assertThat(bpTransitiveEntity).isNotNull(); - - verify(getRequestedFor(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity"))); - } - - @Test - void testChildEntityFromGetByKey() - { - final String resonseEntityWithRelated = - createPayloadSingle(createTestEntity("72", createRelatedEntity("13857", null))); - stubFor( - get(urlPathMatching(SERVICE_B + "/A_EntitySet\\('(.*)'\\)")).willReturn(okJson(resonseEntityWithRelated))); - - final String responseEntityListTransitive = createPayloadMultiple(createTransitiveEntity("Bar")); - stubFor( - get(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity")) - .willReturn(okJson(responseEntityListTransitive))); - - final TestEntity testEntity = - FluentHelperFactory - .withServicePath(SERVICE_B) - .readByKey(TestEntity.class, "A_EntitySet", Collections.singletonMap("EntityId", "72")) - .select(TestEntity.TO_RELATED_ENTITY) - .executeRequest(destination); - - final RelatedEntity bpRelatedEntity = testEntity.getRelatedEntity(); - assertThat(bpRelatedEntity).isNotNull(); - - final List bpTransitiveEntity = bpRelatedEntity.fetchTransitiveEntity(); - assertThat(bpTransitiveEntity).isNotNull(); - - verify(getRequestedFor(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity"))); - } - - @Test - void testChildEntityFromCreate() - { - final String resonseEntityWithRelated = - createPayloadSingle(createTestEntity("72", createRelatedEntity("13857", null))); - stubFor(post(urlPathMatching(SERVICE_B + "/A_EntitySet")).willReturn(okJson(resonseEntityWithRelated))); - - final String responseEntityListTransitive = createPayloadMultiple(createTransitiveEntity("Bar")); - stubFor( - get(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity")) - .willReturn(okJson(responseEntityListTransitive))); - - TestEntity testEntity = new TestEntity(); - - testEntity = - FluentHelperFactory - .withServicePath(SERVICE_B) - .create("A_EntitySet", testEntity) - .executeRequest(destination) - .getModifiedEntity(); - - final RelatedEntity bpRelatedEntity = testEntity.getRelatedEntity(); - assertThat(bpRelatedEntity).isNotNull(); - - final List bpTransitiveEntity = bpRelatedEntity.fetchTransitiveEntity(); - assertThat(bpTransitiveEntity).isNotNull(); - - verify(getRequestedFor(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity"))); - } - - @Test - void testChildEntityFromUpdate() - { - final String responseEntity = createPayloadSingle(createTestEntity("72", null)); - stubFor(get(urlPathMatching(SERVICE_B + "/A_EntitySet\\('(.*)'\\)")).willReturn(okJson(responseEntity))); - - final String responseEntityWithRelated = - createPayloadSingle(createTestEntity("72", createRelatedEntity("13857", null))); - stubFor( - patch(urlPathMatching(SERVICE_B + "/A_EntitySet\\('(.*)'\\)")) - .willReturn(okJson(responseEntityWithRelated))); - - final String responseEntityListTransitive = createPayloadMultiple(createTransitiveEntity("Bar")); - stubFor( - get(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity")) - .willReturn(okJson(responseEntityListTransitive))); - - TestEntity testEntity = - FluentHelperFactory - .withServicePath(SERVICE_B) - .readByKey(TestEntity.class, "A_EntitySet", Collections.singletonMap("EntityId", "72")) - .executeRequest(destination); - - testEntity.setRelatedEntity(new RelatedEntity()); - - testEntity = - FluentHelperFactory - .withServicePath(SERVICE_B) - .update("A_EntitySet", testEntity) - .modifyingEntity() - .executeRequest(destination) - .getModifiedEntity(); - - final RelatedEntity bpRelatedEntity = testEntity.getRelatedEntity(); - assertThat(bpRelatedEntity).isNotNull(); - - final List bpTransitiveEntity = bpRelatedEntity.fetchTransitiveEntity(); - assertThat(bpTransitiveEntity).isNotNull(); - - verify(getRequestedFor(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity"))); - } - - @Test - void testChildEntityFromNavigationProperty() - { - final String responseEntity = createPayloadSingle(createTestEntity("72", null)); - stubFor(get(urlPathMatching(SERVICE_B + "/A_EntitySet\\('(.*)'\\)")).willReturn(okJson(responseEntity))); - - final String responseEntityWithRelated = createPayloadSingle(createRelatedEntity("13857", null)); - stubFor( - get(urlPathMatching(SERVICE_B + "/A_EntitySet\\('(.*)'\\)/to_RelatedEntity")) - .willReturn(okJson(responseEntityWithRelated))); - - final String responseEntityListTransitive = createPayloadMultiple(createTransitiveEntity("Bar")); - stubFor( - get(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity")) - .willReturn(okJson(responseEntityListTransitive))); - - final TestEntity testEntity = - FluentHelperFactory - .withServicePath(SERVICE_B) - .readByKey(TestEntity.class, "A_EntitySet", Collections.singletonMap("EntityId", "72")) - .executeRequest(destination); - - final RelatedEntity bpRelatedEntity = testEntity.fetchRelatedEntity(); - assertThat(bpRelatedEntity).isNotNull(); - - final List bpTransitiveEntity = bpRelatedEntity.fetchTransitiveEntity(); - assertThat(bpTransitiveEntity).isNotNull(); - - verify(getRequestedFor(urlPathMatching(SERVICE_B + "/A_RelatedEntitySet\\('(.*)'\\)/to_TransitiveEntity"))); - } - - @Test - void testChildEntityFromFunctionImport() - throws IOException - { - final String responseFunctionImport = - createPayloadMultiple(createTransitiveEntity("100"), createTransitiveEntity("101")); - stubFor( - get(urlPathMatching(SERVICE_A + "/FunctionToMedia")) - .withQueryParam("Param1", equalTo("'MARA'")) - .withQueryParam("Param2", equalTo("'SAPTEST'")) - .willReturn(okJson(responseFunctionImport))); - - stubFor( - get(urlPathMatching(SERVICE_A + "/A_MediaEntitySet\\((.*)\\)/\\$value")) - .willReturn(okForContentType("text/plain", "Test file content"))); - - final Map parameters = new LinkedHashMap<>(); - parameters.put("Param1", "MARA"); - parameters.put("Param2", "SAPTEST"); - - final List attachments = - FluentHelperFactory - .withServicePath(SERVICE_A) - .functionMultipleGet(parameters, "FunctionToMedia", MediaEntity.class) - .executeRequest(destination); - - for( final MediaEntity attachmentContent : attachments ) { - try( final InputStream attachmentMedia = attachmentContent.fetchMediaStream() ) { - assertThat(attachmentMedia).isNotNull(); - assertThat(attachmentMedia.available()).isGreaterThan(0); - } - } - } - - private static String createPayloadSingle( final String payload ) - { - return format("{\"d\":%s}", payload); - } - - private static String createPayloadMultiple( final String... payloads ) - { - return createPayloadSingle(format("{\"results\":[%s]}", String.join(",", payloads))); - } - - private static String createTestEntity( final String id, final String rel ) - { - final String uri = format("https://127.0.0.1/path/to/serviceB/A_EntitySet('%s')", id); - final String m = format("{\"id\":\"%s\",\"uri\":\"%s\",\"type\":\"ServiceB.A_EntityType\"}", uri, uri); - final String r = rel != null ? rel : format("{\"__deferred\":{\"uri\":\"%s/to_RelatedEntity\"}}", uri); - return format("{\"__metadata\":%s,\"EntityId\":\"%s\",\"to_RelatedEntity\":%s}", m, id, r); - } - - private static String createRelatedEntity( final String id, final String trn ) - { - final String uri = format("https://127.0.0.1/path/to/serviceB/A_RelatedEntitySet('%s')", id); - final String m = format("{\"id\":\"%s\",\"uri\":\"%s\",\"type\":\"ServiceB.A_RelatedType\"}", uri, uri); - final String t = trn != null ? trn : format("{\"__deferred\":{\"uri\":\"%s/to_TransitiveEntity\"}}", uri); - return format("{\"__metadata\":%s,\"RelatedId\":\"%s\",\"to_TransitiveEntity\":%s}", m, id, t); - } - - private static String createTransitiveEntity( final String id ) - { - final String uri = format("https://127.0.0.1/path/to/serviceB/A_TransitiveSet('%s')", id); - final String format = - "{\"id\":\"%s\",\"uri\":\"%s\",\"type\":\"ServiceB.A_MediaType\",\"content_type\":\"text/plain\",\"media_src\":\"%s/$value\"}"; - return format("{\"__metadata\":%s,\"MediaId\":\"%s\"}", format(format, uri, uri, uri), id); - } - - @Data - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class TestEntity extends VdmEntity - { - private final String entityCollection = "A_EntitySet"; - private final Class type = TestEntity.class; - - @SerializedName( "EntityId" ) - @JsonProperty( "EntityId" ) - @ODataField( odataName = "EntityId" ) - private String entityId; - - @SerializedName( "to_RelatedEntity" ) - @JsonProperty( "to_RelatedEntity" ) - @ODataField( odataName = "to_RelatedEntity" ) - private RelatedEntity relatedEntity; - - public static final TestEntityLink TO_RELATED_ENTITY = new TestEntityLink<>("to_RelatedEntity"); - - @Nonnull - @Override - protected Map getKey() - { - return ImmutableMap.of("EntityId", getEntityId()); - } - - @Nonnull - @Override - protected Map toMapOfNavigationProperties() - { - return Collections.singletonMap("to_RelatedEntity", relatedEntity); - } - - public RelatedEntity fetchRelatedEntity() - { - return fetchFieldAsSingle("to_RelatedEntity", RelatedEntity.class); - } - - public void setRelatedEntity( final RelatedEntity relatedEntity ) - { - rememberChangedField("to_RelatedEntity", this.relatedEntity); - this.relatedEntity = relatedEntity; - } - } - - @Data - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class RelatedEntity extends VdmEntity - { - private final String entityCollection = "A_RelatedEntitySet"; - private final Class type = RelatedEntity.class; - - @SerializedName( "RelatedId" ) - @JsonProperty( "RelatedId" ) - @ODataField( odataName = "RelatedId" ) - private String relatedId; - - @SerializedName( "to_TransitiveEntity" ) - @JsonProperty( "to_TransitiveEntity" ) - @ODataField( odataName = "to_TransitiveEntity" ) - private List transitiveEntity; - - public static final RelatedEntityLink TO_RELATED_ENTITY = - new RelatedEntityLink<>("to_TransitiveEntity"); - - @Nonnull - @Override - protected Map getKey() - { - return ImmutableMap.of("RelatedId", getRelatedId()); - } - - @Nonnull - @Override - protected Map toMapOfNavigationProperties() - { - return Collections.singletonMap("to_TransitiveEntity", transitiveEntity); - } - - public List fetchTransitiveEntity() - { - return fetchFieldAsList("to_TransitiveEntity", TransitiveEntity.class); - } - } - - @Data - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class TransitiveEntity extends VdmEntity - { - private final String entityCollection = "A_TransitiveEntitySet"; - private final Class type = TransitiveEntity.class; - } - - @Data - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class MediaEntity extends VdmMediaEntity - { - private final String entityCollection = "A_MediaEntitySet"; - private final Class type = MediaEntity.class; - - @SerializedName( "MediaId" ) - @JsonProperty( "MediaId" ) - @ODataField( odataName = "MediaId" ) - private String mediaId; - - } - - public static class TestEntityLink> - extends - EntityLink, TestEntity, ObjectT> - { - public TestEntityLink( final String name ) - { - super(name); - } - } - - public static class RelatedEntityLink> - extends - EntityLink, TestEntity, ObjectT> - { - public RelatedEntityLink( final String name ) - { - super(name); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/SpecialCharactersTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/SpecialCharactersTest.java deleted file mode 100644 index 1d93574ada..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/SpecialCharactersTest.java +++ /dev/null @@ -1,252 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static com.github.tomakehurst.wiremock.matching.RequestPatternBuilder.allRequests; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.common.collect.ImmutableMap; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@WireMockTest -class SpecialCharactersTest -{ - private static final String SERVICE_URL = "/service/path"; - - private DefaultHttpDestination destination; - - @BeforeEach - void before( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - } - - @Data - @EqualsAndHashCode( callSuper = true ) - public static class Ticket1 extends VdmEntity - { - private final String entityCollection = "Tickets"; - private final Class type = Ticket1.class; - private String keyVal1 = "Val1"; - @Getter( lazy = true ) - private final Map key = ImmutableMap.of("Key1", keyVal1); - } - - @Data - @EqualsAndHashCode( callSuper = true ) - public static class Ticket2 extends VdmEntity - { - private final String entityCollection = "Tickets"; - private final Class type = Ticket2.class; - private String keyVal1 = "Val1"; - private String keyVal2 = "Val2"; - @Getter( lazy = true ) - private final Map key = ImmutableMap.of("Key1", keyVal1, "Key2", keyVal2); - } - - @Test - void testGetByKey() - { - final String keyVal1 = "F?oo"; - final String keyVal2 = "A/ #"; - final String ticketsUrl = SERVICE_URL + "/Tickets(Key1='F%3Foo',Key2='A%2F%20%23')"; - stubFor(get(urlEqualTo(ticketsUrl)).willReturn(okJson("{\"d\":{\"Foo\":\"0\"}}"))); - - final Map key = ImmutableMap.of("Key1", keyVal1, "Key2", keyVal2); - final Ticket2 item = - FluentHelperFactory - .withServicePath(SERVICE_URL) - .readByKey(Ticket2.class, "Tickets", key) - .executeRequest(destination); - - assertThat(item).isNotNull(); - verify(1, allRequests()); - verify(1, getRequestedFor(urlEqualTo(ticketsUrl))); - } - - @Test - void testGetNavigationPropertyByKey() - { - final String ticketsUrl = SERVICE_URL + "/Tickets(Key1='s4L3s%2F%200rd3%3Fr',Key2='1t3%23m')/to_NextTicket"; - - stubFor(get(urlEqualTo(ticketsUrl)).willReturn(okJson("{\"d\":{\"\":\"\"}}"))); - - final Ticket2 item = new Ticket2(); - item.setKeyVal1("s4L3s/ 0rd3?r"); - item.setKeyVal2("1t3#m"); - - item.setDestinationForFetch(destination); - item.setServicePathForFetch(SERVICE_URL); - final Ticket2 nextTicket = item.fetchFieldAsSingle("to_NextTicket", Ticket2.class); - - assertThat(nextTicket).isNotNull(); - assertThat(nextTicket.getDestinationForFetch()).isEqualTo(destination); - verify(1, allRequests()); - verify(1, getRequestedFor(urlEqualTo(ticketsUrl))); - } - - @Test - void testFilterExpression() - { - final String field1Val = "&?:/"; - final String field2Val = "#/\\"; - - final String ticketsUrl = - SERVICE_URL + "/Tickets?$filter=(Field1%20eq%20'%26%3F:/')%20and%20(Field2%20eq%20'%23/%5C')"; - - stubFor(get(urlEqualTo(ticketsUrl)).willReturn(okJson("{\"d\":{\"results\":[{\"Foo\":\"0\"}]}}"))); - - final EntityField field1 = new EntityField<>("Field1"); - final EntityField field2 = new EntityField<>("Field2"); - final Ticket2 item = - FluentHelperFactory - .withServicePath(SERVICE_URL) - .read(Ticket2.class, "Tickets") - .filter(field1.eq(field1Val).and(field2.eq(field2Val))) - .executeRequest(destination) - .get(0); - - assertThat(item).isNotNull(); - verify(1, allRequests()); - verify(1, getRequestedFor(urlEqualTo(ticketsUrl))); - } - - @Test - void testEscapedNestedSingleQuote() - { - // test escaping for get by filter - { - final String getByFilter = "/Tickets?$filter=Field1%20eq%20'foo''bar'"; - stubFor( - get(urlEqualTo(SERVICE_URL + getByFilter)) - .willReturn(okJson("{\"d\":{\"results\":[{\"Foo\":\"foo'bar\"}]}}"))); - - final EntityField field1 = new EntityField<>("Field1"); - final Ticket1 item = - FluentHelperFactory - .withServicePath(SERVICE_URL) - .read(Ticket1.class, "Tickets") - .filter(field1.eq("foo'bar")) - .executeRequest(destination) - .get(0); - assertThat(item).isNotNull(); - verify(1, getRequestedFor(urlEqualTo(SERVICE_URL + getByFilter))); - } - - // test escaping for get by key - { - final String getByKey = "/Tickets('foo''bar')"; - stubFor(get(urlEqualTo(SERVICE_URL + getByKey)).willReturn(okJson("{\"d\":{\"Foo\":\"foo'bar\"}}"))); - - final Map key = ImmutableMap.of("Field1", "foo'bar"); - final Ticket1 item = - FluentHelperFactory - .withServicePath(SERVICE_URL) - .readByKey(Ticket1.class, "Tickets", key) - .executeRequest(destination); - assertThat(item).isNotNull(); - verify(1, getRequestedFor(urlEqualTo(SERVICE_URL + getByKey))); - } - - // test escaping for lazily fetching a navigation property - { - final String getByFilter = "/Tickets('foo''bar')/to_NextTicket"; - stubFor( - get(urlEqualTo(SERVICE_URL + getByFilter)) - .willReturn(okJson("{\"d\":{\"results\":[{\"Foo\":\"0\"}]}}"))); - final Ticket1 item = new Ticket1(); - item.setKeyVal1("foo'bar"); - - item.setDestinationForFetch(destination); - item.setServicePathForFetch(SERVICE_URL); - final Ticket1 nextTicket = item.fetchFieldAsSingle("to_NextTicket", Ticket1.class); - - assertThat(nextTicket).isNotNull(); - assertThat(nextTicket.getDestinationForFetch()).isEqualTo(destination); - verify(1, getRequestedFor(urlEqualTo(SERVICE_URL + getByFilter))); - } - - } - - @Test - void testEscapedOnlySingleQuote() - { - final String oderKey = "'"; - final String itemKey = "'"; - stubFor(head(urlEqualTo(SERVICE_URL)).willReturn(ok())); - - // test escaping for get by filter - { - final String getByFilter = "/Tickets?$filter=(Field1%20eq%20'''')%20and%20(Field2%20eq%20'''')"; - stubFor( - get(urlEqualTo(SERVICE_URL + getByFilter)) - .willReturn(okJson("{\"d\":{\"results\":[{\"Foo\":\"0\"}]}}"))); - - final EntityField field1 = new EntityField<>("Field1"); - final EntityField field2 = new EntityField<>("Field2"); - final Ticket2 item = - FluentHelperFactory - .withServicePath(SERVICE_URL) - .read(Ticket2.class, "Tickets") - .filter(field1.eq(oderKey).and(field2.eq(itemKey))) - .executeRequest(destination) - .get(0); - - assertThat(item).isNotNull(); - verify(1, getRequestedFor(urlEqualTo(SERVICE_URL + getByFilter))); - } - - // test escaping for lazily fetching a navigation property - { - final String getByFilter = "/Tickets(Key1='''',Key2='''')/to_NextTicket"; - stubFor( - get(urlEqualTo(SERVICE_URL + getByFilter)) - .willReturn(okJson("{\"d\":{\"results\":[{\"Foo\":\"0\"}]}}"))); - final Ticket2 item = new Ticket2(); - item.setKeyVal1(oderKey); - item.setKeyVal2(itemKey); - - item.setDestinationForFetch(destination); - item.setServicePathForFetch(SERVICE_URL); - final Ticket2 nextTicket = item.fetchFieldAsSingle("to_NextTicket", Ticket2.class); - - assertThat(nextTicket).isNotNull(); - assertThat(nextTicket.getDestinationForFetch()).isEqualTo(destination); - verify(1, getRequestedFor(urlEqualTo(SERVICE_URL + getByFilter))); - } - - // test escaping for get by key - { - final String getByKey = "/Tickets(Key1='''',Key2='''')"; - stubFor(get(urlEqualTo(SERVICE_URL + getByKey)).willReturn(okJson("{\"d\":{\"Foo\":\"0\"}}"))); - - final Map key = ImmutableMap.of("Key1", oderKey, "Key2", itemKey); - final Ticket2 item = - FluentHelperFactory - .withServicePath(SERVICE_URL) - .readByKey(Ticket2.class, "Tickets", key) - .executeRequest(destination); - assertThat(item).isNotNull(); - verify(getRequestedFor(urlEqualTo(SERVICE_URL + getByKey))); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/TestVdmComplex.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/TestVdmComplex.java deleted file mode 100644 index 1169888fc1..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/TestVdmComplex.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) -public class TestVdmComplex extends VdmComplex -{ - @SerializedName( "SomeValue" ) - @JsonProperty( "SomeValue" ) - @Nullable - @ODataField( odataName = "SomeValue" ) - private String someValue; - - @SerializedName( "OtherValue" ) - @JsonProperty( "OtherValue" ) - @Nullable - @ODataField( odataName = "OtherValue" ) - private String otherValue; - - @SerializedName( "ComplexValue" ) - @JsonProperty( "ComplexValue" ) - @Nullable - @ODataField( odataName = "ComplexValue" ) - private TestVdmComplex complexValue; - - @Getter - private final Class type = TestVdmComplex.class; - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("SomeValue", getSomeValue()); - values.put("OtherValue", getOtherValue()); - values.put("ComplexValue", getComplexValue()); - return values; - } - - public void setSomeValue( String value ) - { - rememberChangedField("SomeValue", someValue); - someValue = value; - } - - public void setOtherValue( String value ) - { - rememberChangedField("OtherValue", otherValue); - otherValue = value; - } - - public void setComplexValue( TestVdmComplex value ) - { - rememberChangedField("ComplexValue", complexValue); - complexValue = value; - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/TestVdmEntity.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/TestVdmEntity.java deleted file mode 100644 index fc79b7fa18..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/TestVdmEntity.java +++ /dev/null @@ -1,298 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZonedDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Lists; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; -import com.sap.cloud.sdk.s4hana.datamodel.odata.annotation.Key; - -import io.vavr.control.Option; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) -public class TestVdmEntity extends VdmEntity -{ - @Getter - final String entityCollection = "Entities"; - - @Getter - private final String defaultServicePath = "/service"; - - @Getter - private final Class type = TestVdmEntity.class; - - @Key - @SerializedName( "IntegerValue" ) - @JsonProperty( "IntegerValue" ) - @Nullable - @ODataField( odataName = "IntegerValue" ) - private Integer integerValue; - - @SerializedName( "GuidValue" ) - @JsonProperty( "GuidValue" ) - @Nullable - @ODataField( odataName = "GuidValue" ) - private UUID guidValue; - - @SerializedName( "StringValue" ) - @JsonProperty( "StringValue" ) - @Nullable - @ODataField( odataName = "StringValue" ) - private String stringValue; - - @SerializedName( "OffsetDateTimeValue" ) - @JsonProperty( "OffsetDateTimeValue" ) - @Nullable - @JsonSerialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonZonedDateTimeSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonZonedDateTimeDeserializer.class ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ZonedDateTimeAdapter.class ) - @ODataField( - odataName = "OffsetDateTimeValue", - converter = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ZonedDateTimeCalendarConverter.class ) - private ZonedDateTime offsetDateTimeValue; - - @SerializedName( "to_Parent" ) - @JsonProperty( "to_Parent" ) - @ODataField( odataName = "to_Parent" ) - @Nullable - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private TestVdmEntity toParent; - - @SerializedName( "to_Children" ) - @JsonProperty( "to_Children" ) - @ODataField( odataName = "to_Children" ) - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private List toChildren; - - @SerializedName( "DecimalValue" ) - @JsonProperty( "DecimalValue" ) - @Nullable - @ODataField( odataName = "DecimalValue" ) - private BigDecimal decimalValue; - - @SerializedName( "DoubleValue" ) - @JsonProperty( "DoubleValue" ) - @Nullable - @ODataField( odataName = "DoubleValue" ) - private Double doubleValue; - - @SerializedName( "LocalTimeValue" ) - @JsonProperty( "LocalTimeValue" ) - @Nullable - @JsonSerialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonLocalTimeSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonLocalTimeDeserializer.class ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalTimeAdapter.class ) - @ODataField( - odataName = "LocalTimeValue", - converter = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalTimeCalendarConverter.class ) - private LocalTime localTimeValue; - - @SerializedName( "LocalDateTimeValue" ) - @JsonProperty( "LocalDateTimeValue" ) - @Nullable - @JsonSerialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonLocalDateTimeSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.JacksonLocalDateTimeDeserializer.class ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalDateTimeAdapter.class ) - @ODataField( - odataName = "LocalDateTimeValue", - converter = com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.LocalDateTimeCalendarConverter.class ) - private LocalDateTime localDateTimeValue; - - @SerializedName( "BooleanValue" ) - @JsonProperty( "BooleanValue" ) - @Nullable - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataBooleanAdapter.class ) - @ODataField( odataName = "BooleanValue" ) - private Boolean booleanValue; - - @SerializedName( "ComplexValue" ) - @JsonProperty( "ComplexValue" ) - @Nullable - @ODataField( odataName = "ComplexValue" ) - private TestVdmComplex complexValue; - - public void setIntegerValue( @Nullable final Integer integerValue ) - { - rememberChangedField("IntegerValue", this.integerValue); - this.integerValue = integerValue; - } - - public void setBooleanValue( @Nullable final Boolean booleanValue ) - { - rememberChangedField("BooleanValue", this.booleanValue); - this.booleanValue = booleanValue; - } - - public void setGuidValue( @Nullable final UUID guidValue ) - { - rememberChangedField("GuidValue", this.guidValue); - this.guidValue = guidValue; - } - - public void setStringValue( @Nullable final String stringValue ) - { - rememberChangedField("StringValue", this.stringValue); - this.stringValue = stringValue; - } - - public void setDecimalValue( @Nullable final BigDecimal decimalValue ) - { - rememberChangedField("DecimalValue", this.decimalValue); - this.decimalValue = decimalValue; - } - - public void setDoubleValue( @Nullable final Double doubleValue ) - { - rememberChangedField("DoubleValue", this.doubleValue); - this.doubleValue = doubleValue; - } - - public void setOffsetDateTimeValue( @Nullable final ZonedDateTime offsetDateTimeValue ) - { - rememberChangedField("OffsetDateTimeValue", this.offsetDateTimeValue); - this.offsetDateTimeValue = offsetDateTimeValue; - } - - public void setLocalTimeValue( @Nullable final LocalTime localTimeValue ) - { - rememberChangedField("LocalTimeValue", this.localTimeValue); - this.localTimeValue = localTimeValue; - } - - public void setLocalDateTimeValue( @Nullable final LocalDateTime localDateTimeValue ) - { - rememberChangedField("LocalDateTimeValue", this.localDateTimeValue); - this.localDateTimeValue = localDateTimeValue; - } - - public void setComplexValue( @Nullable final TestVdmComplex complexValue ) - { - rememberChangedField("ComplexValue", this.complexValue); - this.complexValue = complexValue; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("IntegerValue", getIntegerValue()); - values.put("BooleanValue", getBooleanValue()); - values.put("StringValue", getStringValue()); - values.put("DecimalValue", getDecimalValue()); - values.put("DoubleValue", getDoubleValue()); - values.put("GuidValue", getGuidValue()); - values.put("OffsetDateTimeValue", getOffsetDateTimeValue()); - values.put("LocalDateTimeValue", getLocalDateTimeValue()); - values.put("LocalTimeValue", getLocalTimeValue()); - values.put("ComplexValue", getComplexValue()); - return values; - } - - @Nullable - public List fetchToChildren() - { - // not implemented here - return null; - } - - @Nonnull - public List getToChildrenOrFetch() - { - if( toChildren == null ) { - toChildren = fetchToChildren(); - } - return toChildren; - } - - @Nonnull - public Option> getToChildrenIfPresent() - { - return Option.of(toChildren); - } - - public void setToChildren( @Nonnull final List value ) - { - // rememberChangedField("to_Children", toChildren); - if( toChildren == null ) { - toChildren = Lists.newArrayList(); - } - toChildren.clear(); - toChildren.addAll(value); - } - - public void addToChildren( TestVdmEntity... entity ) - { - // rememberChangedField("to_Children", toChildren); - if( toChildren == null ) { - toChildren = Lists.newArrayList(); - } - toChildren.addAll(Lists.newArrayList(entity)); - } - - @Nullable - public TestVdmEntity fetchToParent() - { - // not implemented here - return null; - } - - @Nonnull - public TestVdmEntity getToParentOrFetch() - { - if( toParent == null ) { - toParent = fetchToParent(); - } - return toParent; - } - - @Nonnull - public Option getToParentIfPresent() - { - return Option.of(toParent); - } - - public void setToParent( @Nonnull final TestVdmEntity value ) - { - // rememberChangedField("to_Children", toChildren); - toParent = value; - } - - @Nonnull - @Override - protected Map getKey() - { - return Collections.singletonMap("IntegerValue", integerValue); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/UncheckedFilterExpressionTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/UncheckedFilterExpressionTest.java deleted file mode 100644 index 6b8d4db145..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/UncheckedFilterExpressionTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static org.assertj.core.api.Assertions.assertThat; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; - -import lombok.Getter; - -class UncheckedFilterExpressionTest -{ - @Test - void testClientFilterExpressionEmpty() - { - final ValueBoolean filterExpression = ValueBoolean.literal(true); - final ExpressionFluentHelper customFilterExpression = new ExpressionFluentHelper<>(filterExpression); - - assertThat( - customFilterExpression.getDelegateExpressionWithoutOuterParentheses().getExpression(ODataProtocol.V2)) - .isEqualTo("true"); - } - - @Test - void testClientFilterExpressionInteger() - { - final ValueBoolean filterExpression = FieldReference.of("ShoeSize").equalTo(42); - final ExpressionFluentHelper customFilterExpression = new ExpressionFluentHelper<>(filterExpression); - - assertThat( - customFilterExpression.getDelegateExpressionWithoutOuterParentheses().getExpression(ODataProtocol.V2)) - .isEqualTo("ShoeSize eq 42"); - - final ODataRequestRead read = newFluentHelperRead().filter(customFilterExpression).toRequest(); - assertThat(read.getRelativeUri()).hasQuery("$filter=ShoeSize eq 42"); - } - - @Test - void testClientFilterExpressionString() - { - final ValueBoolean filterExpression = FieldReference.of("FirstName").equalTo("Alice"); - final ExpressionFluentHelper customFilterExpression = new ExpressionFluentHelper<>(filterExpression); - - assertThat( - customFilterExpression.getDelegateExpressionWithoutOuterParentheses().getExpression(ODataProtocol.V2)) - .isEqualTo("FirstName eq 'Alice'"); - - final ODataRequestRead read = newFluentHelperRead().filter(customFilterExpression).toRequest(); - assertThat(read.getRelativeUri()).hasQuery("$filter=FirstName eq 'Alice'"); - } - - // test helper classes - - private static < - T extends FluentHelperRead> - FluentHelperRead - newFluentHelperRead() - { - return FluentHelperFactory.withServicePath("some/path").read(MyEntity.class, "MyEntityCollection"); - } - - static class MyEntity extends VdmEntity - { - @Getter - private final String entityCollection = "MyEntityCollection"; - - @Getter - private final String defaultServicePath = "API_MY_ENTITY"; - - @Nonnull - @Override - public Class getType() - { - return MyEntity.class; - } - } - - static class MySelectable - { - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmComplexTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmComplexTest.java deleted file mode 100644 index 0872b98ede..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmComplexTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; - -@WireMockTest -class VdmComplexTest -{ - private static final String RESPONSE_CREATE_ENTITY = """ - {"d":{ - "__metadata": { - "id": "https://127.0.0.1/path/to/service(100)", - "uri": "https://127.0.0.1/path/to/service(100)", - "type": "SERVICE.SomeEntity" - }, - "IntegerValue":100, - "StringValue":"Foo", - "ComplexValue":{ - "__metadata": { - "type": "SERVICE.SomeComplex" - }, - "SomeValue":"Some", - "OtherValue":"Another" - } - }} - """; - - private static final String REQUEST_CREATE_ENTITY = """ - { - "StringValue" : "Foo", - "ComplexValue" : { - "SomeValue" : "Some", - "OtherValue" : "Another" - } - } - """; - - private HttpDestination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - } - - @Test - void testCreateEntityWithComplexProperty() - { - stubFor(head(anyUrl()).willReturn(noContent())); - stubFor(post(anyUrl()).willReturn(okJson(RESPONSE_CREATE_ENTITY))); - - final TestVdmComplex complex = new TestVdmComplex(); - complex.setSomeValue("Some"); - complex.setOtherValue("Another"); - - final TestVdmEntity entity = new TestVdmEntity(); - entity.setStringValue("Foo"); - entity.setComplexValue(complex); - - final TestVdmEntity resultEntity = - FluentHelperFactory - .withServicePath("/path/to/service") - .create(entity.getEntityCollection(), entity) - .executeRequest(destination) - .getModifiedEntity(); - - assertThat(resultEntity).isNotNull().isNotEqualTo(entity); - assertThat(resultEntity.getComplexValue()).isEqualTo(complex); - - verify(headRequestedFor(urlPathEqualTo("/path/to/service/")).withHeader("x-csrf-token", equalTo("fetch"))); - verify( - postRequestedFor(urlPathEqualTo("/path/to/service/Entities")) - .withRequestBody(equalToJson(REQUEST_CREATE_ENTITY))); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntityTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntityTest.java deleted file mode 100644 index 9ad5073129..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntityTest.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static org.assertj.core.api.Assertions.assertThat; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationProperty; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestinationProperties; - -class VdmEntityTest -{ - private static final String TEST_DEFAULT_SERVICE_PATH = "/odata/default"; - private static final String TEST_DESTINATION_NAME = "UnitTestDestination"; - - private static HttpDestinationProperties TEST_DESTINATION; - - private static class TestEntity extends VdmEntity - { - @Override - protected String getEntityCollection() - { - return null; - } - - @Nonnull - @Override - public Class getType() - { - return TestEntity.class; - } - - @Override - protected String getDefaultServicePath() - { - return TEST_DEFAULT_SERVICE_PATH; - } - } - - @BeforeAll - static void setUp() - { - TEST_DESTINATION = DefaultHttpDestination.builder("").name(TEST_DESTINATION_NAME).build(); - } - - @Test - void testAttachToErpWithDefaults() - { - final TestEntity sut = new TestEntity(); - sut.attachToService(null, (HttpDestinationProperties) null); - - assertThat(sut.getServicePathForFetch()).isNotEmpty(); - assertThat(sut.getServicePathForFetch()).isEqualTo(TEST_DEFAULT_SERVICE_PATH); - - assertThat(sut.getDestinationForFetch()).isNull(); - } - - @Test - void testAttachToErpWithCustomErpConfigContext() - { - final TestEntity sut = new TestEntity(); - sut.attachToService(null, TEST_DESTINATION); - - assertThat(sut.getServicePathForFetch()).isNotEmpty(); - assertThat(sut.getServicePathForFetch()).isEqualTo(TEST_DEFAULT_SERVICE_PATH); - - assertThat(sut.getDestinationForFetch()).isNotNull(); - assertThat(sut.getDestinationForFetch().get(DestinationProperty.NAME)).contains(TEST_DESTINATION_NAME); - } - - @Test - void testAttachToErpWithCustomServicePath() - { - final TestEntity sut = new TestEntity(); - sut.attachToService("/sap/opu/odata", (HttpDestinationProperties) null); - - assertThat(sut.getServicePathForFetch()).isNotEmpty(); - assertThat(sut.getServicePathForFetch()).isEqualTo("/sap/opu/odata"); - - assertThat(sut.getDestinationForFetch()).isNull(); - } - - @Test - void testAttachToErpWithAllCustomParameters() - { - final TestEntity sut = new TestEntity(); - sut.attachToService("/sap/opu/odata", TEST_DESTINATION); - - assertThat(sut.getServicePathForFetch()).isNotEmpty(); - assertThat(sut.getServicePathForFetch()).isEqualTo("/sap/opu/odata"); - - assertThat(sut.getDestinationForFetch()).isNotNull(); - assertThat(sut.getDestinationForFetch().get(DestinationProperty.NAME)).contains(TEST_DESTINATION_NAME); - } - - @Test - void testEntityComparison() - { - final TestVdmEntity foo1 = TestVdmEntity.builder().stringValue("foo").build(); - final TestVdmEntity foo2 = TestVdmEntity.builder().stringValue("foo").build(); - assertThat(foo1) - .withFailMessage( - "Entities wit equal properties should be equal. Expected:\n %s\n to be equal to:\n %s\nbut was not.", - foo1, - foo2) - .isEqualTo(foo2); - - foo1.setVersionIdentifier("1"); - foo2.setVersionIdentifier("1"); - assertThat(foo1).withFailMessage("Equal entities with equal ETags should be equal.").isEqualTo(foo2); - - foo1.setServicePathForFetch("bar"); - foo2.setServicePathForFetch("baz"); - assertThat(foo1) - .withFailMessage("Equal entities with different service paths should be equal.") - .isEqualTo(foo2); - - foo1.setDestinationForFetch(DefaultHttpDestination.builder("bar").build()); - foo2.setDestinationForFetch(DefaultHttpDestination.builder("baz").build()); - assertThat(foo1) - .withFailMessage("Equal entities with different service paths should be equal.") - .isEqualTo(foo2); - - foo2.setVersionIdentifier("2"); - assertThat(foo1).withFailMessage("Equal entities with different ETags should not be equal.").isNotEqualTo(foo2); - } - - @Test - void testChangedNonCustomFields() - { - final TestVdmEntity entity = TestVdmEntity.builder().stringValue("old").build(); - - assertThat(entity.getChangedFields()).isEmpty(); - - entity.setStringValue("old"); - assertThat(entity.getChangedFields()).isEmpty(); - - entity.setStringValue("new"); - assertThat(entity.getChangedFields()).containsOnlyKeys("StringValue"); - } - - @Test - void testChangedCustomFields() - { - final TestVdmEntity entity = TestVdmEntity.builder().build(); - - assertThat(entity.getChangedFields()).isEmpty(); - - entity.setCustomField("foo", "bar"); - assertThat(entity.getChangedFields()).containsOnlyKeys("foo"); - - entity.resetChangedFields(); - entity.setCustomField("foo", "bar"); - assertThat(entity.getChangedFields()).isEmpty(); - - entity.setCustomField("foo", "baz"); - assertThat(entity.getChangedFields()).containsOnlyKeys("foo"); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntityVersionIdentifierTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntityVersionIdentifierTest.java deleted file mode 100644 index d86d22ed62..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/VdmEntityVersionIdentifierTest.java +++ /dev/null @@ -1,354 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper; - -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.patch; -import static com.github.tomakehurst.wiremock.client.WireMock.patchRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.junit5.WireMockExtension; -import com.google.common.collect.ImmutableMap; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataResponseException; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataField; -import com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory; - -import io.vavr.control.Option; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.With; - -class VdmEntityVersionIdentifierTest -{ - @RegisterExtension - static final WireMockExtension ERP_SERVER = - WireMockExtension.newInstance().options(wireMockConfig().dynamicPort().gzipDisabled(true)).build(); - - private static final String key1val = "2015"; - private static final String key2val = "100000000"; - private static final String key3val = "1"; - private static final Map KEY_MAP = - ImmutableMap.of("Key1", key1val, "Key2", key2val, "Key3", key3val); - - private static final String versionIdentifierInResponseBody = - "W/\"datetimeoffset'2018-01-09T08%3A33%3A53.8828600Z'\""; - private static final String versionIdentifierInHeader = "W/\"datetimeoffset'2015-01-09T08%3A33%3A53.8828600Z'\""; - private static final String updatedVersionIdentifier = "W/\"datetimeoffset'2015-01-09T08%3A33%3A53.8828600Z'\""; - - private static final String ODATA_ENDPOINT_URL = "/path/to/service"; - private static final String ODATA_COLLECTION = "A_TestEntity"; - private static final String ODATA_DOCUMENT_ITEM_URL = - String - .format( - "%s/%s(Key1='%s',Key2='%s',Key3='%s')", - ODATA_ENDPOINT_URL, - ODATA_COLLECTION, - key1val, - key2val, - key3val); - - private static final String getDocumentItemResponseBody = """ - { - "d": { - "__metadata": { - "id": "https://127.0.0.1/path/to/service/A_TestEntity(Key1='2018',Key2='100010641',Key3='1')", - "uri": "https://127.0.0.1/path/to/service/A_TestEntity(Key1='2018',Key2='100010641',Key3='1')", - "type": "SERVICE.A_TestEntityType", - "etag": "W/\\"datetimeoffset'2018-01-09T08%3A33%3A53.8828600Z'\\"" - }, - "Key1": "2015", - "Key2": "100000000", - "Key3": "1", - "SomeField": "Foo" - } - } - """; - - @Data - @NoArgsConstructor - @AllArgsConstructor( access = AccessLevel.PRIVATE ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( ODataVdmEntityAdapterFactory.class ) - public static class TestEntity extends VdmEntity - { - private final String entityCollection = ODATA_COLLECTION; - private final Class type = TestEntity.class; - - @With - @SerializedName( "Key1" ) - @JsonProperty( "Key1" ) - @ODataField( odataName = "Key1" ) - private String key1; - - @With - @SerializedName( "Key2" ) - @JsonProperty( "Key2" ) - @ODataField( odataName = "Key2" ) - private String key2; - - @With - @SerializedName( "Key3" ) - @JsonProperty( "Key3" ) - @ODataField( odataName = "Key3" ) - private String key3; - - @With - @SerializedName( "SomeField" ) - @JsonProperty( "SomeField" ) - @ODataField( odataName = "SomeField" ) - private String someField; - - @Nonnull - @Override - protected Map getKey() - { - return ImmutableMap.of("Key1", getKey1(), "Key2", getKey2(), "Key3", getKey3()); - } - - public void setSomeField( final String someField ) - { - rememberChangedField("SomeField", this.someField); - this.someField = someField; - } - } - - private DefaultHttpDestination destination; - - @BeforeEach - void before() - { - destination = DefaultHttpDestination.builder(ERP_SERVER.baseUrl()).build(); - ERP_SERVER.stubFor(head(urlEqualTo(ODATA_ENDPOINT_URL)).willReturn(WireMock.ok())); - ERP_SERVER - .stubFor( - get(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).willReturn(aResponse().withBody(getDocumentItemResponseBody))); - } - - @Test - void testVersionIdentifier() - { - ERP_SERVER.stubFor(patch(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).willReturn(ok())); - - final TestEntity item = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .readByKey(TestEntity.class, ODATA_COLLECTION, KEY_MAP) - .executeRequest(destination); - - item.setKey1("2015"); - item.setSomeField("Bar"); - - assertThat(item.getVersionIdentifier()).isEqualTo(Option.of(versionIdentifierInResponseBody)); - - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .update(ODATA_COLLECTION, item) - .executeRequest(destination); - - ERP_SERVER - .verify( - patchRequestedFor(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)) - .withHeader("If-Match", equalTo(versionIdentifierInResponseBody))); - } - - @Test - void testVersionIdentifierSetInHeader() - { - ERP_SERVER - .stubFor( - get(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)) - .willReturn( - aResponse() - .withBody(getDocumentItemResponseBody) - .withHeader("ETag", versionIdentifierInHeader))); - ERP_SERVER.stubFor(patch(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).willReturn(ok())); - - final TestEntity item = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .readByKey(TestEntity.class, ODATA_COLLECTION, KEY_MAP) - .executeRequest(destination); - - item.setKey1("2015"); - item.setSomeField("Bar"); - - assertThat(item.getVersionIdentifier()).isEqualTo(Option.of(versionIdentifierInHeader)); - - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .update(ODATA_COLLECTION, item) - .executeRequest(destination); - - ERP_SERVER - .verify( - patchRequestedFor(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)) - .withHeader("If-Match", equalTo(versionIdentifierInHeader))); - } - - @Test - void testIgnoreVersionIdentifier() - { - ERP_SERVER.stubFor(patch(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).willReturn(ok())); - - final TestEntity item = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .readByKey(TestEntity.class, ODATA_COLLECTION, KEY_MAP) - .executeRequest(destination); - - item.setKey1("2015"); - item.setSomeField("Bar"); - - assertThat(item.getVersionIdentifier()).isEqualTo(Option.of(versionIdentifierInResponseBody)); - - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .update(ODATA_COLLECTION, item) - .matchAnyVersionIdentifier() - .executeRequest(destination); - - ERP_SERVER.verify(patchRequestedFor(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).withHeader("If-Match", equalTo("*"))); - } - - @Test - void testIgnoreVersionIdentifierEvenIfNoVersionIdentifierPresent() - { - ERP_SERVER.stubFor(patch(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).willReturn(ok())); - - final TestEntity item = new TestEntity().withKey1(key1val).withKey2(key2val).withKey3(key3val); - - item.setKey1("2015"); - item.setSomeField("Bar"); - - assertThat(item.getVersionIdentifier().isDefined()).isFalse(); - - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .update(ODATA_COLLECTION, item) - .matchAnyVersionIdentifier() - .executeRequest(destination); - - ERP_SERVER.verify(patchRequestedFor(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).withHeader("If-Match", equalTo("*"))); - } - - @Test - void testExceptionWhenExpiredVersionIdentifierIsSent() - { - ERP_SERVER.stubFor(patch(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).willReturn(aResponse().withStatus(412))); - - final TestEntity item = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .readByKey(TestEntity.class, ODATA_COLLECTION, KEY_MAP) - .executeRequest(destination); - - item.setKey1("2015"); - item.setSomeField("Bar"); - - assertThat(item.getVersionIdentifier()).isEqualTo(Option.of(versionIdentifierInResponseBody)); - - item.setVersionIdentifier(updatedVersionIdentifier); - - assertThat(item.getVersionIdentifier()).isEqualTo(Option.of(updatedVersionIdentifier)); - - assertThatThrownBy( - () -> FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .update(ODATA_COLLECTION, item) - .executeRequest(destination)) - .isInstanceOf(ODataResponseException.class) - .extracting("httpCode") - .isEqualTo(412); - } - - @Test - void testExceptionWhenVersionIdentifierMissing() - { - ERP_SERVER.stubFor(patch(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).willReturn(aResponse().withStatus(428))); - - final TestEntity item = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .readByKey(TestEntity.class, ODATA_COLLECTION, KEY_MAP) - .executeRequest(destination); - - item.setKey1("2015"); - item.setSomeField("Bar"); - - assertThat(item.getVersionIdentifier()).isEqualTo(Option.of(versionIdentifierInResponseBody)); - - item.setVersionIdentifier(updatedVersionIdentifier); - - assertThat(item.getVersionIdentifier()).isEqualTo(Option.of(updatedVersionIdentifier)); - - assertThatThrownBy( - () -> FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .update(ODATA_COLLECTION, item) - .executeRequest(destination)) - .isInstanceOf(ODataResponseException.class) - .extracting("httpCode") - .isEqualTo(428); - } - - @Test - void testVersionIdentifierSentAndFetchedEvenAfterUpdate() - { - ERP_SERVER - .stubFor( - get(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)).willReturn(aResponse().withBody(getDocumentItemResponseBody))); - ERP_SERVER - .stubFor( - patch(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)) - .willReturn(ok().withHeader("ETag", versionIdentifierInHeader))); - - final TestEntity item = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .readByKey(TestEntity.class, ODATA_COLLECTION, KEY_MAP) - .executeRequest(destination); - - item.setKey1("2015"); - item.setSomeField("Bar"); - - assertThat(item.getVersionIdentifier()).isEqualTo(Option.of(versionIdentifierInResponseBody)); - - final ModificationResponse updateResponse = - FluentHelperFactory - .withServicePath(ODATA_ENDPOINT_URL) - .update(ODATA_COLLECTION, item) - .executeRequest(destination); - - ERP_SERVER - .verify( - patchRequestedFor(urlEqualTo(ODATA_DOCUMENT_ITEM_URL)) - .withHeader("If-Match", equalTo(versionIdentifierInResponseBody))); - - assertThat(updateResponse.getRequestEntity().getVersionIdentifier()) - .isEqualTo(Option.of(versionIdentifierInResponseBody)); - assertThat(updateResponse.getModifiedEntity().getVersionIdentifier()) - .isEqualTo(Option.of(versionIdentifierInHeader)); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/ODataV2BatchFunctionImportTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/ODataV2BatchFunctionImportTest.java deleted file mode 100644 index 29a9e15a47..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/ODataV2BatchFunctionImportTest.java +++ /dev/null @@ -1,251 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.equalToIgnoreCase; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; - -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.common.collect.ImmutableMap; -import com.google.common.io.Resources; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultMultipartGeneric; -import com.sap.cloud.sdk.datamodel.odata.helper.TestVdmEntity; - -import lombok.SneakyThrows; - -@WireMockTest -class ODataV2BatchFunctionImportTest -{ - private static final String X_CSRF_TOKEN_HEADER_KEY = "x-csrf-token"; - private static final String X_CSRF_TOKEN_HEADER_FETCH_VALUE = "fetch"; - private static final String X_CSRF_TOKEN_HEADER_VALUE = "awesome-csrf-token"; - private static final String REQUEST_URL_BATCH = "/$batch"; - private static final String RESPONSE_BODY = readResourceFileCrlf("BatchResponse.txt"); - private static final String REQUEST_BODY_POST = readResourceFileCrlf("BatchRequestFunctionImportWithPost.txt"); - private static final String REQUEST_BODY_GET = readResourceFileCrlf("BatchRequestFunctionImportWithGet.txt"); - private static final String REQUEST_BODY_POST_WITH_CUSTOM_HEADER = - readResourceFileCrlf("BatchRequestFunctionImportWithPostWithCustomHeader.txt"); - - @BeforeEach - void before() - { - stubFor( - head(urlEqualTo("/")) - .withHeader(X_CSRF_TOKEN_HEADER_KEY, equalToIgnoreCase(X_CSRF_TOKEN_HEADER_FETCH_VALUE)) - .willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, X_CSRF_TOKEN_HEADER_VALUE))); - - final String contentType = "multipart/mixed; boundary=batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef"; - stubFor(post(urlEqualTo(REQUEST_URL_BATCH)).willReturn(okForContentType(contentType, RESPONSE_BODY))); - } - - @Test - void testIllegalStateExceptionWhenFunctionImportUsingHttpGetInChangeSet( @Nonnull final WireMockRuntimeInfo wm ) - { - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - final TestVdmEntityBatch.TestFunctionImportSingleResultHttpGet functionImport = - new TestVdmEntityBatch.TestFunctionImportSingleResultHttpGet("John", "Doe"); - - assertThatIllegalStateException().isThrownBy(() -> { - new TestVdmEntityBatch("") - .beginChangeSet() - .addFunctionImport(functionImport) - .endChangeSet() - .executeRequest(destination); - }); - } - - @Test - void testFunctionImportUsingHttpPostInChangeSet( @Nonnull final WireMockRuntimeInfo wm ) - { - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - final TestVdmEntityBatch.TestFunctionImportHttpPost functionImport = - new TestVdmEntityBatch.TestFunctionImportHttpPost("John", "Doe"); - - new TestVdmEntityBatch("") - .beginChangeSet() - .addFunctionImport(functionImport) - .endChangeSet() - .executeRequest(destination); - - verify(postRequestedFor(urlPathEqualTo(REQUEST_URL_BATCH)).withRequestBody(equalTo(REQUEST_BODY_POST))); - } - - @Test - void testFunctionImportUsingHttpGetInBatchRequest( @Nonnull final WireMockRuntimeInfo wm ) - { - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - final TestVdmEntityBatch.TestFunctionImportSingleResultHttpGet functionImport = - new TestVdmEntityBatch.TestFunctionImportSingleResultHttpGet("John", "Doe"); - - new TestVdmEntityBatch("").addReadOperations(functionImport).executeRequest(destination); - - verify(postRequestedFor(urlPathEqualTo(REQUEST_URL_BATCH)).withRequestBody(equalTo(REQUEST_BODY_GET))); - } - - @Test - void - testIllegalStateExceptionWhenFunctionImportUsingHttpPostInReadOperation( @Nonnull final WireMockRuntimeInfo wm ) - { - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - final TestVdmEntityBatch.TestFunctionImportHttpPost functionImport = - new TestVdmEntityBatch.TestFunctionImportHttpPost("John", "Doe"); - - assertThatIllegalStateException().isThrownBy(() -> { - new TestVdmEntityBatch("").addReadOperations(functionImport).executeRequest(destination); - }); - } - - @Test - void testFunctionImportUsingHttpPostInChangeSetWithCustomHeader( @Nonnull final WireMockRuntimeInfo wm ) - { - final DefaultHttpDestination destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - final TestVdmEntityBatch.TestFunctionImportHttpPost functionImport = - new TestVdmEntityBatch.TestFunctionImportHttpPost("John", "Doe").withHeader("foo", "bar"); - - new TestVdmEntityBatch("") - .beginChangeSet() - .addFunctionImport(functionImport) - .endChangeSet() - .executeRequest(destination); - - verify( - postRequestedFor(urlPathEqualTo(REQUEST_URL_BATCH)) - .withRequestBody(equalTo(REQUEST_BODY_POST_WITH_CUSTOM_HEADER))); - } - - @SneakyThrows - private static String readResourceFileCrlf( final String resourceFileName ) - { - final URL resourceUrl = - ODataV2BatchRequestUnitTest.class - .getClassLoader() - .getResource(ODataV2BatchFunctionImportTest.class.getSimpleName() + "/" + resourceFileName); - final String result = Resources.toString(resourceUrl, StandardCharsets.UTF_8); - return result.replaceAll("(? "StringValue", () -> "IntegerValue") - .top(10) - .withHeader("header-read_all", "read_all"); - - final TestVdmEntityBatch.TestFunctionImportSingleResultHttpGet testFunctionImportSingleResultHttpGet = - new TestVdmEntityBatch.TestFunctionImportSingleResultHttpGet("Alice", "Bob"); - - final TestVdmEntityBatch.TestFunctionImportCollectionResultHttpGet testFunctionImportCollectionResultHttpGet = - new TestVdmEntityBatch.TestFunctionImportCollectionResultHttpGet(); - - final TestVdmEntityBatch.TestFunctionImportHttpPost functionImport = - new TestVdmEntityBatch.TestFunctionImportHttpPost("John", "Doe"); - - final TestVdmEntityBatch.TestFunctionImportSingleEntityResultHttpGet testFunctionImportSingleEntityResultHttpGet = - new TestVdmEntityBatch.TestFunctionImportSingleEntityResultHttpGet(33, "Alice"); - - final TestVdmEntityBatch batchBuilder = - testVdmEntityBatch - .addReadOperations(readAll) - .addChangeSet(create) - .addReadOperations(entityByKey) - .addChangeSet(update, delete) - .beginChangeSet() - .create(testVdmEntity) - .addFunctionImport(functionImport) - .endChangeSet() - .addReadOperations(testFunctionImportSingleResultHttpGet, testFunctionImportCollectionResultHttpGet) - .addReadOperations(testFunctionImportSingleEntityResultHttpGet); - - final ODataRequestBatch lowLevelRequest = batchBuilder.toRequest(); - - final HttpClient httpClient = ApacheHttpClient5Accessor.getHttpClient(destination); - - final ODataRequestResultMultipartGeneric lowLevelResult = lowLevelRequest.execute(httpClient); - - //Customer transition example from low-level result object to typed result - - final DefaultBatchResponseResult convertedHighLevelResult = - DefaultBatchResponseResult.of(lowLevelResult, batchBuilder); - - final BatchResponse expectedHighLevelResult = batchBuilder.executeRequest(destination); - - assertThat(expectedHighLevelResult.getReadResult(readAll)) - .isEqualTo(convertedHighLevelResult.getReadResult(readAll)); - - assertThat(expectedHighLevelResult.getReadResult(entityByKey)) - .isEqualTo(convertedHighLevelResult.getReadResult(entityByKey)); - assertThat(expectedHighLevelResult.getReadResult(entityByKey)) - .extracting(TestVdmEntity::getIntegerValue) - .isEqualTo(9000); - - assertThat(expectedHighLevelResult.getReadResult(testFunctionImportSingleResultHttpGet)) - .isEqualTo(convertedHighLevelResult.getReadResult(testFunctionImportSingleResultHttpGet)); - assertThat(expectedHighLevelResult.getReadResult(testFunctionImportSingleResultHttpGet)) - .isEqualTo("awesomeStuff"); - - assertThat(expectedHighLevelResult.getReadResult(testFunctionImportCollectionResultHttpGet)) - .isEqualTo(convertedHighLevelResult.getReadResult(testFunctionImportCollectionResultHttpGet)); - assertThat(expectedHighLevelResult.getReadResult(testFunctionImportCollectionResultHttpGet)) - .isEqualTo(Arrays.asList("foo", "bar")); - - assertThat(expectedHighLevelResult.getReadResult(testFunctionImportSingleEntityResultHttpGet)) - .isEqualTo(convertedHighLevelResult.getReadResult(testFunctionImportSingleEntityResultHttpGet)); - assertThat(expectedHighLevelResult.getReadResult(testFunctionImportSingleEntityResultHttpGet)) - .extracting(TestVdmEntity::getIntegerValue) - .isEqualTo(33); - assertThat(expectedHighLevelResult.getReadResult(testFunctionImportSingleEntityResultHttpGet)) - .extracting(TestVdmEntity::getStringValue) - .isEqualTo("Alice"); - - assertThat(expectedHighLevelResult.get(0).get().getCreatedEntities()) - .isEqualTo(convertedHighLevelResult.get(0).get().getCreatedEntities()); - assertThat(expectedHighLevelResult.get(1).get().getCreatedEntities()) - .isEqualTo(convertedHighLevelResult.get(1).get().getCreatedEntities()); - assertThat(expectedHighLevelResult.get(2).isFailure()).isEqualTo(convertedHighLevelResult.get(2).isFailure()); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/ODataV2BatchRequestUnitTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/ODataV2BatchRequestUnitTest.java deleted file mode 100644 index a5b8c62f3e..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/ODataV2BatchRequestUnitTest.java +++ /dev/null @@ -1,368 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import static com.github.tomakehurst.wiremock.client.WireMock.equalToIgnoreCase; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.matching; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.when; - -import java.io.InputStream; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.client5.http.classic.methods.HttpPost; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.io.entity.InputStreamEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mockito; - -import com.github.tomakehurst.wiremock.WireMockServer; -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.google.common.collect.ImmutableMap; -import com.google.common.io.Resources; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5FactoryBuilder; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestinationProperties; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataServiceErrorException; -import com.sap.cloud.sdk.datamodel.odata.helper.TestVdmEntity; - -import lombok.SneakyThrows; -import lombok.Value; - -class ODataV2BatchRequestUnitTest -{ - private static final WireMockConfiguration WIREMOCK_CONFIGURATION = wireMockConfig().dynamicPort(); - private static final String X_CSRF_TOKEN_HEADER_KEY = "x-csrf-token"; - private static final String X_CSRF_TOKEN_HEADER_FETCH_VALUE = "fetch"; - private static final String X_CSRF_TOKEN_HEADER_VALUE = "awesome-csrf-token"; - private static final String REQUEST_URL_BATCH = "/$batch"; - - private static final String REQUEST_BODY = readResourceFileCrlf("BatchRequest.txt"); - private static final String REQUEST_BODY_WITH_CUSTOM_HEADERS = - readResourceFileCrlf("BatchRequestWithCustomHeaders.txt"); - private static final String RESPONSE_BODY = readResourceFileCrlf("BatchResponse.txt"); - private static final int MAX_PARALLEL_CONNECTIONS = 10; - - private final WireMockServer server = new WireMockServer(WIREMOCK_CONFIGURATION); - private DefaultHttpDestination destination; - - private static Stream getTestParameters() - { - return Stream.of(new TestParameter("#executeRequest", BatchFluentHelperBasic::executeRequest, REQUEST_BODY)); - } - - private static Stream getTestParametersWithCustomHeaders() - { - return Stream - .of( - new TestParameter( - "#executeRequest", - BatchFluentHelperBasic::executeRequest, - REQUEST_BODY_WITH_CUSTOM_HEADERS)); - } - - @Value - static class TestParameter - { - String label; - Executor executor; - String requestBody; - - public String toString() - { - return label; - } - - interface Executor - { - BatchResponse execute( BatchFluentHelperBasic fluentHelper, HttpDestinationProperties destination ) - throws Exception; - } - } - - @BeforeEach - void setup() - { - server.start(); - destination = DefaultHttpDestination.builder(server.baseUrl()).build(); - - // Mock CSRF token handling - server - .stubFor( - head(urlEqualTo("/")) - .withHeader(X_CSRF_TOKEN_HEADER_KEY, equalToIgnoreCase(X_CSRF_TOKEN_HEADER_FETCH_VALUE)) - .willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, X_CSRF_TOKEN_HEADER_VALUE))); - - // Mock OData Batch response - final String contentType = "multipart/mixed; boundary=batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef"; - server.stubFor(post(urlEqualTo(REQUEST_URL_BATCH)).willReturn(okForContentType(contentType, RESPONSE_BODY))); - - ODataApacheHttpClient5Accessor - .setHttpClientFactory( - new ApacheHttpClient5FactoryBuilder() - .withCsrfTokenInterceptor() - .maxConnectionsTotal(MAX_PARALLEL_CONNECTIONS) - .maxConnectionsPerRoute(MAX_PARALLEL_CONNECTIONS) - .build()); - } - - @AfterEach - void shutdown() - { - server.shutdown(); - ODataApacheHttpClient5Accessor.setHttpClientFactory(null); - } - - @SneakyThrows - @ParameterizedTest( name = "{0}" ) - @MethodSource( "getTestParameters" ) - void testAllOperations( @Nonnull final TestParameter parameter ) - { - final TestVdmEntity entity12 = TestVdmEntity.builder().integerValue(12).build(); - final TestVdmEntity entity13 = TestVdmEntity.builder().integerValue(13).build(); - final TestVdmEntity entity14 = TestVdmEntity.builder().integerValue(14).build(); - final TestVdmEntity entity15 = TestVdmEntity.builder().integerValue(15).build(); - final TestVdmEntity entity16 = TestVdmEntity.builder().integerValue(16).build(); - - final TestVdmEntityBatch.TestEntityRead readAll = - new TestVdmEntityBatch.TestEntityRead().select(() -> "StringValue", () -> "IntegerValue").top(10); - final TestVdmEntityBatch.TestEntityByKey readByKey = - new TestVdmEntityBatch.TestEntityByKey(ImmutableMap.of("IntegerValue", 9000)); - - // prepare batched requests - final TestVdmEntityBatch request = - new TestVdmEntityBatch("") - - // read operation - .addReadOperations(readAll) - - // changeset:0 - .beginChangeSet() - .create(entity12) - .endChangeSet() - - // read operation - .addReadOperations(readByKey) - - // changeset:1 - .beginChangeSet() - .update(entity13) - .delete(entity14) - .endChangeSet() - - // changeset:2 - .beginChangeSet() - .delete(entity15) - .delete(entity16) - .endChangeSet(); - - // execute batch request with try-with-resources to ensure no connection leaks - for( int i = 0; i <= MAX_PARALLEL_CONNECTIONS * 2; i++ ) { - try( final BatchResponse response = request.executeRequest(destination) ) { - verifyBatchChangeSets(response); - verifyBatchReadAll(response, readAll); - verifyBatchReadByKey(response, readByKey); - verifyBatchRequestBody(parameter); - } - } - } - - @SneakyThrows - @ParameterizedTest( name = "{0}" ) - @MethodSource( "getTestParametersWithCustomHeaders" ) - void testAllOperationsWithCustomHeaders( @Nonnull final TestParameter parameter ) - { - final TestVdmEntity entity12 = TestVdmEntity.builder().integerValue(12).build(); - final TestVdmEntity entity13 = TestVdmEntity.builder().integerValue(13).build(); - final TestVdmEntity entity14 = TestVdmEntity.builder().integerValue(14).build(); - final TestVdmEntity entity15 = TestVdmEntity.builder().integerValue(15).build(); - final TestVdmEntity entity16 = TestVdmEntity.builder().integerValue(16).build(); - - final TestVdmEntityBatch.TestEntityRead readAll = - new TestVdmEntityBatch.TestEntityRead() - .select(() -> "StringValue", () -> "IntegerValue") - .top(10) - .withHeader("header-read_all", "read_all"); - final TestVdmEntityBatch.TestEntityByKey readByKey = - new TestVdmEntityBatch.TestEntityByKey(ImmutableMap.of("IntegerValue", 9000)) - .withHeader("header-read_by_key", "read_by_key"); - final TestVdmEntityBatch.TestEntityCreate create = - new TestVdmEntityBatch.TestEntityCreate(entity12).withHeader("header-create", "create"); - final TestVdmEntityBatch.TestEntityUpdate update = - new TestVdmEntityBatch.TestEntityUpdate(entity13).withHeader("header-update", "update"); - final TestVdmEntityBatch.TestEntityDelete deleteEntity14 = - new TestVdmEntityBatch.TestEntityDelete(entity14).withHeader("header-delete", "delete-entity14"); - final TestVdmEntityBatch.TestEntityDelete deleteEntity15 = - new TestVdmEntityBatch.TestEntityDelete(entity15).withHeader("header-delete", "delete-entity15"); - final TestVdmEntityBatch.TestEntityDelete deleteEntity16 = - new TestVdmEntityBatch.TestEntityDelete(entity16).withHeader("header-delete", "delete-entity16"); - - // prepare batched requests - final TestVdmEntityBatch request = - new TestVdmEntityBatch("") - - // read operation - .addReadOperations(readAll) - - // changeset:0 - .addChangeSet(create) - - // read operation - .addReadOperations(readByKey) - - // changeset:1 - .addChangeSet(update, deleteEntity14) - - // changeset:2 - .addChangeSet(deleteEntity15, deleteEntity16); - - for( int i = 0; i < MAX_PARALLEL_CONNECTIONS * 2; i++ ) { - // execute batch request - final BatchResponse response = parameter.getExecutor().execute(request, destination); - - verifyBatchChangeSets(response); - verifyBatchReadAll(response, readAll); - verifyBatchReadByKey(response, readByKey); - verifyBatchRequestBody(parameter); - } - } - - private void verifyBatchChangeSets( @Nonnull final BatchResponse response ) - { - // assertion on response parsing - assertThat(response.get(0).isSuccess()).isTrue(); - assertThat(response.get(0).get().getCreatedEntities()).hasSize(1); - assertThat(response.get(1).isSuccess()).isTrue(); - assertThat(response.get(1).get().getCreatedEntities()).hasSize(0); - - assertThat(response.get(2).isSuccess()).isFalse(); // legitimately no success: unhealthy response - assertThat(response.get(2).getCause()).isInstanceOfAny(ODataServiceErrorException.class); - assertThat(response.get(3).isSuccess()).isFalse(); // index out of bounds - assertThat(response.get(3).getCause()).isInstanceOf(IllegalArgumentException.class); - } - - private void verifyBatchReadAll( - @Nonnull final BatchResponse response, - @Nonnull final TestVdmEntityBatch.TestEntityRead readAll ) - { - assertThat(response.getReadResult(readAll)) - .satisfiesExactly(item -> assertThat(item.getStringValue()).isEqualTo("Foo")); - } - - private void verifyBatchReadByKey( - @Nonnull final BatchResponse response, - @Nonnull final TestVdmEntityBatch.TestEntityByKey readByKey ) - { - assertThat(response.getReadResult(readByKey)).extracting(TestVdmEntity::getIntegerValue).isEqualTo(9000); - } - - private void verifyBatchRequestBody( @Nonnull final TestParameter parameter ) - { - server - .verify( - postRequestedFor(urlEqualTo(REQUEST_URL_BATCH)) - .withHeader("Content-Type", matching("multipart/mixed;boundary=batch_[a-z0-9-]+")) - .withHeader(X_CSRF_TOKEN_HEADER_KEY, equalToIgnoreCase(X_CSRF_TOKEN_HEADER_VALUE)) - .withRequestBody(matching("\\Q" + parameter.getRequestBody() + "\\E"))); - } - - @SneakyThrows - private static String readResourceFileCrlf( final String resourceFileName ) - { - final URL resourceUrl = - ODataV2BatchRequestUnitTest.class - .getClassLoader() - .getResource(ODataV2BatchRequestUnitTest.class.getSimpleName() + "/" + resourceFileName); - final String result = Resources.toString(resourceUrl, StandardCharsets.UTF_8); - return result.replaceAll("(? inputStreams = new ArrayList<>(); - - final HttpDestination dest = DefaultHttpDestination.builder("").build(); - final HttpClient httpClient = mock(HttpClient.class); - when(httpClient.executeOpen(isNull(), argThat(req -> req instanceof HttpPost), isNull())).thenAnswer(args -> { - final BasicClassicHttpResponse response = new BasicClassicHttpResponse(200, "ok"); - final InputStream inStream = mock(InputStream.class); - inputStreams.add(inStream); - response.setEntity(new InputStreamEntity(inStream, ContentType.APPLICATION_JSON)); - return response; - }); - - // configure test setup - ODataApacheHttpClient5Accessor.setHttpClientFactory(( anyDestination ) -> httpClient); - - // TEST: invoke many batch request each spawning an InputStream - for( int i = 0; i < N; i++ ) { - new TestVdmEntityBatch("").addReadOperations(new TestVdmEntityBatch.TestEntityRead()).executeRequest(dest); - } - - // ASSERTION: input streams are loaded but never fully consumed - assertThat(inputStreams).hasSize(N); - for( final InputStream inStream : inputStreams ) { - Mockito.verify(inStream, never()).close(); - } - - // TEST: invoke one batch request using try-with-resource - try( - BatchResponse result = - new TestVdmEntityBatch("") - .addReadOperations(new TestVdmEntityBatch.TestEntityRead()) - .executeRequest(dest) ) { - - assertThat(result).isNotNull(); - } - - // ASSERTION: input stream is fully consumed - Mockito.verify(inputStreams.get(N), times(1)).close(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/ODatav2BatchConnectionTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/ODatav2BatchConnectionTest.java deleted file mode 100644 index 30a0d15a16..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/ODatav2BatchConnectionTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.sap.cloud.sdk.datamodel.odata.helper.batch.TestVdmEntityBatch.TestEntityByKey; -import static com.sap.cloud.sdk.datamodel.odata.helper.batch.TestVdmEntityBatch.TestEntityCreate; -import static com.sap.cloud.sdk.datamodel.odata.helper.batch.TestVdmEntityBatch.TestEntityDelete; -import static com.sap.cloud.sdk.datamodel.odata.helper.batch.TestVdmEntityBatch.TestEntityRead; -import static com.sap.cloud.sdk.datamodel.odata.helper.batch.TestVdmEntityBatch.TestEntityUpdate; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nonnull; - -import org.apache.hc.core5.http.ConnectionRequestTimeoutException; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.github.tomakehurst.wiremock.matching.UrlPattern; -import com.google.common.collect.ImmutableMap; -import com.google.common.io.Resources; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5FactoryBuilder; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataConnectionException; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataServiceErrorException; -import com.sap.cloud.sdk.datamodel.odata.helper.TestVdmEntity; - -import lombok.SneakyThrows; - -@WireMockTest -class ODatav2BatchConnectionTest -{ - private static final String RESPONSE_CONTENT_TYPE = - "multipart/mixed; boundary=batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef"; - private static final String RESPONSE_WITH_CHANGESET = readResourceFileCrlf("BatchResponseWithChangeset.txt"); - private static final String RESPONSE_WITHOUT_CHANGESET = readResourceFileCrlf("BatchResponseWithoutChangeset.txt"); - private static final String RESPONSE_WITH_ERROR = readResourceFileCrlf("BatchResponseWithError.txt"); - private static final int MAX_PARALLEL_CONNECTIONS = 10; - private DefaultHttpDestination destination; - - @SneakyThrows - private static String readResourceFileCrlf( final String resourceFileName ) - { - final URL resourceUrl = - ODatav2BatchConnectionTest.class - .getClassLoader() - .getResource(ODatav2BatchConnectionTest.class.getSimpleName() + "/" + resourceFileName); - final String result = Resources.toString(resourceUrl, StandardCharsets.UTF_8); - return result.replaceAll("(? result = batchResponse.getReadResult(read); - assertThat(result).isNotEmpty(); - final TestVdmEntity readByKeyResult = batchResponse.getReadResult(readByKey); - assertThat(readByKeyResult).extracting(TestVdmEntity::getIntegerValue).isEqualTo(9000); - } - } - - @Test - void testNoConnectionTimeoutWhenBatchResponseContainsChangeset() - { - stubFor(post(UrlPattern.ANY).willReturn(okForContentType(RESPONSE_CONTENT_TYPE, RESPONSE_WITH_CHANGESET))); - - final TestEntityRead read = new TestEntityRead(); - final TestEntityByKey readByKey = new TestEntityByKey(ImmutableMap.of("IntegerValue", 9000)); - final TestEntityCreate create = new TestEntityCreate(TestVdmEntity.builder().integerValue(1337).build()); - final TestVdmEntity updateEntity = TestVdmEntity.builder().integerValue(13).build(); - final TestEntityUpdate update = new TestEntityUpdate(updateEntity); - final TestVdmEntity deleteEntity = TestVdmEntity.builder().integerValue(14).build(); - final TestEntityDelete delete = new TestEntityDelete(deleteEntity); - final TestEntityCreate createAnother = new TestEntityCreate(TestVdmEntity.builder().integerValue(1447).build()); - - for( int i = 0; i < MAX_PARALLEL_CONNECTIONS * 2; i++ ) { - final TestVdmEntityBatch request = - new TestVdmEntityBatch("") - .addReadOperations(read) - .addChangeSet(create) - .addReadOperations(readByKey) - .addChangeSet(update, delete, createAnother); - final BatchResponse batchResponse = request.executeRequest(destination); - final List result = batchResponse.getReadResult(read); - assertThat(result).isNotEmpty(); - - final TestVdmEntity readByKeyResult = batchResponse.getReadResult(readByKey); - assertThat(readByKeyResult).extracting(TestVdmEntity::getIntegerValue).isEqualTo(9000); - - assertThat(batchResponse.get(0).isSuccess()).isTrue(); - assertThat(batchResponse.get(0).get().getCreatedEntities()).hasSize(1); - assertThat(batchResponse.get(1).isSuccess()).isTrue(); - assertThat(batchResponse.get(1).get().getCreatedEntities()).hasSize(1); - } - } - - @Test - void testNoConnectionTimeoutWhenBatchResponseContainsError() - { - stubFor(post(UrlPattern.ANY).willReturn(okForContentType(RESPONSE_CONTENT_TYPE, RESPONSE_WITH_ERROR))); - - final TestEntityRead read = new TestEntityRead(); - final TestEntityByKey readByKey = new TestEntityByKey(ImmutableMap.of("IntegerValue", 9000)); - final TestVdmEntity deleteEntity = TestVdmEntity.builder().integerValue(14).build(); - final TestEntityDelete delete = new TestEntityDelete(deleteEntity); - - for( int i = 0; i < MAX_PARALLEL_CONNECTIONS * 2; i++ ) { - final TestVdmEntityBatch request = - new TestVdmEntityBatch("").addReadOperations(read).addReadOperations(readByKey).addChangeSet(delete); - final BatchResponse batchResponse = request.executeRequest(destination); - final List result = batchResponse.getReadResult(read); - assertThat(result).isNotEmpty(); - final TestVdmEntity readByKeyResult = batchResponse.getReadResult(readByKey); - assertThat(readByKeyResult).extracting(TestVdmEntity::getIntegerValue).isEqualTo(9000); - assertThat(batchResponse.get(0).isSuccess()).isFalse(); - assertThat(batchResponse.get(0).getCause()).isInstanceOfAny(ODataServiceErrorException.class); - } - } - - @Test - @Timeout( value = 300_000L, unit = TimeUnit.MILLISECONDS ) - @Disabled( "Test triggers a ConnectionRequestTimeoutException. Use it only to manually verify behaviour." ) - void testConnectionTimeoutWhenBatchResponseIsNotConsumedFully() - { - stubFor(post(UrlPattern.ANY).willReturn(okForContentType(RESPONSE_CONTENT_TYPE, RESPONSE_WITH_CHANGESET))); - - final TestEntityRead read = new TestEntityRead(); - final TestEntityByKey readByKey = new TestEntityByKey(ImmutableMap.of("IntegerValue", 9000)); - final TestEntityCreate create = new TestEntityCreate(TestVdmEntity.builder().integerValue(1337).build()); - final TestVdmEntity updateEntity = TestVdmEntity.builder().integerValue(13).build(); - final TestEntityUpdate update = new TestEntityUpdate(updateEntity); - final TestVdmEntity deleteEntity = TestVdmEntity.builder().integerValue(14).build(); - final TestEntityDelete delete = new TestEntityDelete(deleteEntity); - final TestEntityCreate createAnother = new TestEntityCreate(TestVdmEntity.builder().integerValue(1447).build()); - final TestVdmEntityBatch request = - new TestVdmEntityBatch("") - .addReadOperations(read) - .addChangeSet(create) - .addReadOperations(readByKey) - .addChangeSet(update, delete, createAnother); - - for( int i = 0; i < MAX_PARALLEL_CONNECTIONS; i++ ) { - assertThatNoException().isThrownBy(() -> request.executeRequest(destination)); - } - - assertThatThrownBy(() -> request.executeRequest(destination)) - .isInstanceOf(ODataConnectionException.class) - .hasRootCauseExactlyInstanceOf(ConnectionRequestTimeoutException.class) - .hasMessageContaining( - "Please execute your request with try-with-resources to ensure resources are properly closed."); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/TestVdmEntityBatch.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/TestVdmEntityBatch.java deleted file mode 100644 index a001031e9b..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odata/helper/batch/TestVdmEntityBatch.java +++ /dev/null @@ -1,371 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odata.helper.batch; - -import java.net.URI; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.classic.methods.HttpPost; -import org.apache.hc.client5.http.classic.methods.HttpUriRequest; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.helper.CollectionValuedFluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.EntitySelectable; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperByKey; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperCreate; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperDelete; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperRead; -import com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperUpdate; -import com.sap.cloud.sdk.datamodel.odata.helper.SingleValuedFluentHelperFunction; -import com.sap.cloud.sdk.datamodel.odata.helper.TestVdmEntity; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -class TestVdmEntityBatch extends BatchFluentHelperBasic -{ - @Getter - private final String servicePathForBatchRequest; - - { - final AtomicInteger uuidCounter = new AtomicInteger(); - uuidProvider = () -> new UUID(0, uuidCounter.incrementAndGet()); - } - - @Nonnull - @Override - protected TestVdmEntityBatch getThis() - { - return this; - } - - @Nonnull - @Override - public TestVdmEntityChangeset beginChangeSet() - { - return new TestVdmEntityChangeset(this); - } - - static class TestVdmEntityChangeset - extends - BatchChangeSetFluentHelperBasic - { - public TestVdmEntityChangeset( final TestVdmEntityBatch batch ) - { - super(batch, batch); - } - - @Nonnull - @Override - protected TestVdmEntityChangeset getThis() - { - return this; - } - - TestVdmEntityChangeset create( final TestVdmEntity obj ) - { - return addRequestCreate(TestEntityCreate::new, obj); - } - - TestVdmEntityChangeset update( final TestVdmEntity obj ) - { - return addRequestUpdate(TestEntityUpdate::new, obj); - } - - TestVdmEntityChangeset delete( final TestVdmEntity obj ) - { - return addRequestDelete(TestEntityDelete::new, obj); - } - } - - static class TestEntityByKey - extends - FluentHelperByKey> - { - @Getter - private final Map key; - - @SuppressWarnings( "deprecation" ) - public TestEntityByKey( final Map key ) - { - super("", TestVdmEntity.builder().build().getEntityCollection()); - this.key = key; - } - - @Nonnull - @Override - protected Class getEntityClass() - { - return TestVdmEntity.class; - } - } - - static class TestEntityRead extends FluentHelperRead - { - @SuppressWarnings( "deprecation" ) - public TestEntityRead() - { - super("", TestVdmEntity.builder().build().getEntityCollection()); - } - - @Nonnull - @Override - protected Class getEntityClass() - { - return TestVdmEntity.class; - } - } - - static class TestEntityDelete extends FluentHelperDelete - { - @Getter - private final TestVdmEntity entity; - - @SuppressWarnings( "deprecation" ) - TestEntityDelete( final TestVdmEntity entity ) - { - super("", entity.getEntityCollection()); - this.entity = entity; - } - } - - static class TestEntityUpdate extends FluentHelperUpdate - { - @Getter - private final TestVdmEntity entity; - - @SuppressWarnings( "deprecation" ) - TestEntityUpdate( final TestVdmEntity entity ) - { - super("", entity.getEntityCollection()); - this.entity = entity; - } - } - - static class TestEntityCreate extends FluentHelperCreate - { - @Getter - private final TestVdmEntity entity; - - @SuppressWarnings( "deprecation" ) - TestEntityCreate( final TestVdmEntity entity ) - { - super("", entity.getEntityCollection()); - this.entity = entity; - } - } - - static class TestFunctionImportSingleResultHttpGet - extends - SingleValuedFluentHelperFunction - { - private final Map values = new HashMap<>(); - - @Nonnull - @Override - protected Class getEntityClass() - { - return String.class; - } - - TestFunctionImportSingleResultHttpGet( @Nonnull final String firstName, @Nonnull final String lastName ) - { - super(""); - values.put("FirstName", firstName); - values.put("LastName", lastName); - } - - @Nullable - @Override - public String executeRequest( @Nonnull final Destination destination ) - { - return "awesomeStuff"; - } - - @Nonnull - @Override - protected Map getParameters() - { - return values; - } - - @Nonnull - @Override - protected String getFunctionName() - { - return "awesomeFunction"; - } - - @Nonnull - @Override - protected HttpUriRequest createRequest( @Nonnull final URI uri ) - { - return new HttpGet(uri); - } - } - - static class TestFunctionImportCollectionResultHttpGet - extends - CollectionValuedFluentHelperFunction> - { - private final Map values = new HashMap<>(); - - @Nonnull - @Override - protected Class getEntityClass() - { - return String.class; - } - - TestFunctionImportCollectionResultHttpGet() - { - super(""); - } - - @Nullable - @Override - public List executeRequest( @Nonnull final Destination destination ) - { - return Arrays.asList("foo", "bar"); - } - - @Nonnull - @Override - protected Map getParameters() - { - return values; - } - - @Nonnull - @Override - protected String getFunctionName() - { - return "awesomeFunction"; - } - - @Nonnull - @Override - protected HttpUriRequest createRequest( @Nonnull final URI uri ) - { - return new HttpGet(uri); - } - } - - static class TestFunctionImportSingleEntityResultHttpGet - extends - SingleValuedFluentHelperFunction - { - private final Map values = new HashMap<>(); - - @Nonnull - @Override - protected Class getEntityClass() - { - return TestVdmEntity.class; - } - - TestFunctionImportSingleEntityResultHttpGet( - @Nonnull final int integerValue, - @Nonnull final String stringValue ) - { - super(""); - values.put("IntegerValue", integerValue); - values.put("StringValue", stringValue); - } - - @Nullable - @Override - public TestVdmEntity executeRequest( @Nonnull final Destination destination ) - { - return TestVdmEntity - .builder() - .integerValue((int) values.get("IntegerValue")) - .stringValue((String) values.get("StringValue")) - .build(); - } - - @Nonnull - @Override - protected Map getParameters() - { - return values; - } - - @Nonnull - @Override - protected String getFunctionName() - { - return "awesomeFunction"; - } - - @Nonnull - @Override - protected HttpUriRequest createRequest( @Nonnull final URI uri ) - { - return new HttpGet(uri); - } - } - - static class TestFunctionImportHttpPost - extends - FluentHelperFunction - { - private final Map values = new HashMap<>(); - - TestFunctionImportHttpPost( @Nonnull final String firstName, @Nonnull final String lastName ) - { - super(""); - values.put("FirstName", firstName); - values.put("LastName", lastName); - } - - @Nonnull - @Override - protected Class getEntityClass() - { - return TestVdmEntity.class; - } - - @Nullable - @Override - public String executeRequest( @Nonnull final Destination destination ) - { - return "awesomeStuff"; - } - - @Nonnull - @Override - protected Map getParameters() - { - return values; - } - - @Nonnull - @Override - protected String getFunctionName() - { - return "awesomeFunction"; - } - - @Nonnull - @Override - protected HttpUriRequest createRequest( @Nonnull final URI uri ) - { - return new HttpPost(uri); - } - } - - interface TestVdmEntitySelectable extends EntitySelectable - { - - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalDateTimeAdapterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalDateTimeAdapterTest.java deleted file mode 100644 index ba0d9447ee..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalDateTimeAdapterTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDateTime; -import java.time.Month; - -import org.junit.jupiter.api.Test; - -import com.google.gson.TypeAdapter; - -import lombok.SneakyThrows; - -class LocalDateTimeAdapterTest -{ - @Test - @SneakyThrows - void standardFormatCases() - { - final TypeAdapter adapter = new LocalDateTimeAdapter(); - - assertThat(adapter.fromJson("\"/Date(1649669575192)/\"")).isEqualTo("2022-04-11T09:32:55.192"); - } - - @Test - @SneakyThrows - void wrongFormatReturnsNull() - { - final TypeAdapter adapter = new LocalDateTimeAdapter(); - - assertThat(adapter.fromJson("\"Something that is not a date\"")).isNull(); - } - - @Test - @SneakyThrows - void excessivelyLongNumberReturnsNull() - { - final TypeAdapter adapter = new LocalDateTimeAdapter(); - - assertThat(adapter.fromJson("\"/Date(100000000000000000000000)/\"")).isNull(); - } - - @Test - @SneakyThrows - void nonStringValueReturnsNull() - { - final TypeAdapter adapter = new LocalDateTimeAdapter(); - - assertThat(adapter.fromJson("1234")).isNull(); - } - - @Test - void writeLocalDateTime() - { - final TypeAdapter adapter = new LocalDateTimeAdapter(); - - assertThat(adapter.toJson(LocalDateTime.of(2022, Month.APRIL, 11, 11, 36, 55, 123000000))) - .isEqualTo("\"/Date(1649677015123)/\""); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalTimeAdapterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalTimeAdapterTest.java deleted file mode 100644 index 447c5072fe..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/LocalTimeAdapterTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalTime; - -import org.junit.jupiter.api.Test; - -import com.google.gson.TypeAdapter; - -import lombok.SneakyThrows; - -class LocalTimeAdapterTest -{ - @Test - @SneakyThrows - void standardFormatCases() - { - final TypeAdapter adapter = new LocalTimeAdapter(); - - assertThat(adapter.fromJson("\"PT13H20M\"")).isEqualTo("13:20"); - assertThat(adapter.fromJson("\"PT22M\"")).isEqualTo("00:22"); - assertThat(adapter.fromJson("\"PT15H\"")).isEqualTo("15:00"); - assertThat(adapter.fromJson("\"PT54S\"")).isEqualTo("00:00:54"); - assertThat(adapter.fromJson("\"PT54.123S\"")).isEqualTo("00:00:54.123"); - } - - @Test - @SneakyThrows - void adapterIgnoresDateFields() - { - final TypeAdapter adapter = new LocalTimeAdapter(); - - assertThat(adapter.fromJson("\"P11Y22M33DT13H22M12.345S\"")).isEqualTo("13:22:12.345"); - assertThat(adapter.fromJson("\"P11Y22M33DT\"")).isEqualTo("00:00"); - } - - @Test - @SneakyThrows - void adapterReturnsNullOnEmptyData() - { - final TypeAdapter adapter = new LocalTimeAdapter(); - - assertThat(adapter.fromJson("\"PT\"")).isNull(); - } - - @Test - @SneakyThrows - void nonStringValueReturnsNull() - { - final TypeAdapter adapter = new LocalTimeAdapter(); - - assertThat(adapter.fromJson("1234")).isNull(); - } - - @Test - void writeLocalTime() - { - final TypeAdapter adapter = new LocalTimeAdapter(); - - assertThat(adapter.toJson(LocalTime.of(1, 2, 3))).isEqualTo("\"PT1H2M3S\""); - assertThat(adapter.toJson(LocalTime.of(14, 54, 32, 123000000))).isEqualTo("\"PT14H54M32.123S\""); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataBinaryAdapterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataBinaryAdapterTest.java deleted file mode 100644 index 7982c1feb2..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataBinaryAdapterTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Base64; - -import org.junit.jupiter.api.Test; - -import com.google.gson.TypeAdapter; - -import lombok.SneakyThrows; - -class ODataBinaryAdapterTest -{ - @Test - @SneakyThrows - void readIntoByteArray() - { - final String testText = "Hello World"; - final String base64encodedString = - Base64.getEncoder().encodeToString(testText.getBytes(StandardCharsets.UTF_8)); - - final TypeAdapter binaryAdapter = new ODataBinaryAdapter(); - - final byte[] parsedResult = binaryAdapter.fromJson("\"" + base64encodedString + "\""); - - assertThat(parsedResult).isEqualTo(testText.getBytes(StandardCharsets.UTF_8)); - } - - @Test - @SneakyThrows - void readInvalidString() - { - final TypeAdapter binaryAdapter = new ODataBinaryAdapter(); - - assertThat(binaryAdapter.fromJson("\"_-/!§$&/()\"")).isNull(); - } - - @Test - void writeFromByteArray() - { - final String testText = "Hello World"; - final String base64encodedString = - Base64.getEncoder().encodeToString(testText.getBytes(StandardCharsets.UTF_8)); - - final TypeAdapter binaryAdapter = new ODataBinaryAdapter(); - - final String writtenResult = binaryAdapter.toJson(testText.getBytes(StandardCharsets.UTF_8)); - - assertThat(writtenResult).isEqualTo("\"" + base64encodedString + "\""); - } - - @Test - void nonStringValueGetsIgnored() - throws IOException - { - final TypeAdapter binaryAdapter = new ODataBinaryAdapter(); - - assertThat(binaryAdapter.fromJson("123")).isNull(); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataCustomFieldAdapterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataCustomFieldAdapterTest.java deleted file mode 100644 index e8b8476b51..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataCustomFieldAdapterTest.java +++ /dev/null @@ -1,216 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.io.IOException; -import java.io.StringReader; -import java.time.Instant; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.TimeZone; - -import org.junit.jupiter.api.Test; - -import com.google.gson.Gson; -import com.google.gson.stream.JsonReader; - -class ODataCustomFieldAdapterTest -{ - private static final Gson GSON = new Gson(); - - @Test - void testReadInteger() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader(String.valueOf(Integer.MAX_VALUE))); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - assertThat(sut.read(jsonReader)).isEqualTo(Integer.MAX_VALUE); - } - - @Test - void testReadLong() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader(String.valueOf(Long.MAX_VALUE))); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - assertThat(sut.read(jsonReader)).isEqualTo(Long.MAX_VALUE); - } - - @Test - void testReadDouble() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader(String.valueOf(Double.MAX_VALUE))); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - assertThat(sut.read(jsonReader)).isEqualTo(Double.MAX_VALUE); - } - - @Test - void testReadBoolean() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader(String.valueOf(true))); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - assertThat(sut.read(jsonReader)).isEqualTo(true); - } - - @Test - void testReadParsesDateString() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader("\"/Date(1643775600000)/\"")); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - calendar.setTime(Date.from(Instant.parse("2022-02-02T04:20:00Z"))); - - assertThat(sut.read(jsonReader)).isEqualTo(calendar); - } - - @Test - void testReadMalformedDateStringLeadsToNull() - throws IOException - { - // The given timestamp exceeds the allowed maximum while still conforming to the regex used to match Date strings. - // ==> The input is a "malformed" Date string. - // Therefore, parsing fails and the adapter should return null. - final JsonReader jsonReader = new JsonReader(new StringReader("\"/Date(" + Long.MAX_VALUE + "9)/\"")); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - assertThat(sut.read(jsonReader)).isNull(); - } - - @Test - void testReadDoesntParseOffsetDateTimeString() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader("\"/Date(2022-02-02T04:20:00Z)/\"")); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - assertThat(sut.read(jsonReader)).isEqualTo("/Date(2022-02-02T04:20:00Z)/"); - } - - @Test - void testReadString() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader("\"foo\"")); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - assertThat(sut.read(jsonReader)).isEqualTo("foo"); - } - - @Test - void testReadArrayOfPrimitives() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader("[\"foo\", \"bar\"]")); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - assertThat(sut.read(jsonReader)).isEqualTo(Arrays.asList("foo", "bar")); - } - - @Test - void testReadObjectOfPrimitives() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader("{\"foo\": \"bar\", \"baz\": 42}")); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - final HashMap expected = new HashMap<>(); - expected.put("foo", "bar"); - expected.put("baz", 42); - - assertThat(sut.read(jsonReader)).isEqualTo(expected); - } - - @Test - void testReadDeferredObjectLeadsToNull() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader("{\"__deferred\": \"some value\"}")); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - assertThat(sut.read(jsonReader)).isNull(); - } - - @Test - void testReadSkipsMetadata() - throws IOException - { - final JsonReader jsonReader = - new JsonReader(new StringReader("{\"__metadata\": {\"key\": \"value\"}, \"foo\": \"bar\", \"baz\": 42}")); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - final HashMap expected = new HashMap<>(); - expected.put("foo", "bar"); - expected.put("baz", 42); - - assertThat(sut.read(jsonReader)).isEqualTo(expected); - } - - @Test - void testReadReturnsResultsOnRootLevel() - throws IOException - { - final JsonReader jsonReader = new JsonReader(new StringReader("{\"results\": {\"key\": \"value\"}}")); - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - final HashMap expected = new HashMap<>(); - expected.put("key", "value"); - - assertThat(sut.read(jsonReader)).isEqualTo(expected); - } - - @Test - void testReadExpectsDeferredToBeTheLastObject() - { - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - { - // working example - final JsonReader jsonReader = - new JsonReader(new StringReader("{\"key\": \"value\", \"__deferred\": {\"foo\": \"bar\"}}")); - assertThatNoException().isThrownBy(() -> sut.read(jsonReader)); - } - - { - // failing example - final JsonReader jsonReader = - new JsonReader(new StringReader("{\"__deferred\": {\"foo\": \"bar\"}, \"key\": \"value\"}")); - assertThatThrownBy(() -> sut.read(jsonReader)) - .isExactlyInstanceOf(IllegalStateException.class) - .hasMessageContaining("Expected END_OBJECT but was NAME"); - } - } - - @Test - void testReadExpectsResultsToBeTheLastObject() - { - final ODataCustomFieldAdapter sut = new ODataCustomFieldAdapter(GSON); - - { - // working example - final JsonReader jsonReader = - new JsonReader(new StringReader("{\"key\": \"value\", \"results\": {\"foo\": \"bar\"}}")); - assertThatNoException().isThrownBy(() -> sut.read(jsonReader)); - } - - { - // failing example - final JsonReader jsonReader = - new JsonReader(new StringReader("{\"results\": {\"foo\": \"bar\"}, \"key\": \"value\"}")); - assertThatThrownBy(() -> sut.read(jsonReader)) - .isExactlyInstanceOf(IllegalStateException.class) - .hasMessageContaining("Expected END_OBJECT but was NAME"); - } - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataDateTimeStringCalendarConverterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataDateTimeStringCalendarConverterTest.java deleted file mode 100644 index 9b6486582b..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataDateTimeStringCalendarConverterTest.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Instant; -import java.time.OffsetDateTime; -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; - -import org.junit.jupiter.api.Test; - -class ODataDateTimeStringCalendarConverterTest -{ - @Test - void testDateZeroToCalendar() - { - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - final Calendar calendar = sut.toDomainNonNull("/Date(0)/").orNull(); - assertThat(calendar).isNotNull(); - assertThat(calendar.getTimeInMillis()).isEqualTo(0); - assertThat(calendar.getTimeZone()).isEqualTo(TimeZone.getTimeZone("GMT")); - assertThat(calendar.toInstant()).isEqualTo(Instant.parse("1970-01-01T00:00:00Z")); - } - - @Test - void testDatePositiveToCalendar() - { - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - final Calendar calendar = sut.toDomainNonNull("/Date(1643775600000)/").orNull(); - assertThat(calendar).isNotNull(); - assertThat(calendar.getTimeInMillis()).isEqualTo(1643775600000L); - assertThat(calendar.getTimeZone()).isEqualTo(TimeZone.getTimeZone("GMT")); - assertThat(calendar.toInstant()).isEqualTo(Instant.parse("2022-02-02T04:20:00Z")); - } - - @Test - void testDateNegativeToCalendar() - { - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - final Calendar calendar = sut.toDomainNonNull("/Date(-5480538000)/").orNull(); - assertThat(calendar).isNotNull(); - assertThat(calendar.getTimeInMillis()).isEqualTo(-5480538000L); - assertThat(calendar.getTimeZone()).isEqualTo(TimeZone.getTimeZone("GMT")); - assertThat(calendar.toInstant()).isEqualTo(Instant.parse("1969-10-29T13:37:42Z")); - } - - @Test - void testDateLimitsToCalendar() - { - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - // max - assertThat(sut.toDomainNonNull("/Date(" + Long.MAX_VALUE + ")/").isConvertible()).isTrue(); - // > max - assertThat(sut.toDomainNonNull("/Date(" + Long.MAX_VALUE + "9)/").isConvertible()).isFalse(); - // min - assertThat(sut.toDomainNonNull("/Date(" + Long.MIN_VALUE + ")/").isConvertible()).isTrue(); - // < min - assertThat(sut.toDomainNonNull("/Date(" + Long.MIN_VALUE + "9)/").isConvertible()).isFalse(); - } - - @Test - void testInvalidStringCannotBeConverted() - { - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - assertThat(sut.toDomainNonNull("").isNotConvertible()).isTrue(); - assertThat(sut.toDomainNonNull("Not a number").isNotConvertible()).isTrue(); - assertThat(sut.toDomainNonNull("0").isNotConvertible()).isTrue(); - assertThat(sut.toDomainNonNull("-1").isNotConvertible()).isTrue(); - assertThat(sut.toDomainNonNull("1").isNotConvertible()).isTrue(); - assertThat(sut.toDomainNonNull("Date()").isNotConvertible()).isTrue(); - assertThat(sut.toDomainNonNull("/Date()/").isNotConvertible()).isTrue(); - assertThat(sut.toDomainNonNull("/date(0)/").isNotConvertible()).isTrue(); - assertThat(sut.toDomainNonNull("/Date(-)/").isNotConvertible()).isTrue(); - assertThat(sut.toDomainNonNull("/Date(+0)/").isNotConvertible()).isTrue(); - } - - @Test - void testCalendarZeroToString() - { - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - calendar.setTime(Date.from(Instant.parse("1970-01-01T00:00:00Z"))); - - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - final String dateString = sut.fromDomainNonNull(calendar).orNull(); - assertThat(dateString).isNotNull(); - assertThat(dateString).isEqualTo("/Date(0)/"); - } - - @Test - void testCalendarZeroWithOffsetToString() - { - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+01:00")); - calendar.setTime(Date.from(OffsetDateTime.parse("1970-01-01T00:00:00+01:00").toInstant())); - - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - final String dateString = sut.fromDomainNonNull(calendar).orNull(); - assertThat(dateString).isNotNull(); - assertThat(dateString).isEqualTo("/Date(-3600000)/"); - } - - @Test - void testCalendarPositiveToString() - { - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - calendar.setTime(Date.from(Instant.parse("2022-02-02T04:20:00Z"))); - - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - final String dateString = sut.fromDomainNonNull(calendar).orNull(); - assertThat(dateString).isNotNull(); - assertThat(dateString).isEqualTo("/Date(1643775600000)/"); - } - - @Test - void testCalendarPositiveWithOffsetToString() - { - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT-00:30")); - calendar.setTime(Date.from(OffsetDateTime.parse("2022-02-02T04:20:00-00:30").toInstant())); - - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - final String dateString = sut.fromDomainNonNull(calendar).orNull(); - assertThat(dateString).isNotNull(); - assertThat(dateString).isEqualTo("/Date(1643777400000)/"); - } - - @Test - void testCalendarNegativeToString() - { - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - calendar.setTime(Date.from(Instant.parse("1969-10-29T13:37:42Z"))); - - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - final String dateString = sut.fromDomainNonNull(calendar).orNull(); - assertThat(dateString).isNotNull(); - assertThat(dateString).isEqualTo("/Date(-5480538000)/"); - } - - @Test - void testCalendarNegativeWithOffsetToString() - { - final Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+01:45")); - calendar.setTime(Date.from(OffsetDateTime.parse("1969-10-29T13:37:42+01:45").toInstant())); - - final ODataDateTimeStringCalendarConverter sut = new ODataDateTimeStringCalendarConverter(); - - final String dateString = sut.fromDomainNonNull(calendar).orNull(); - assertThat(dateString).isNotNull(); - assertThat(dateString).isEqualTo("/Date(-5486838000)/"); - } - -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataPrimitiveAdapterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataPrimitiveAdapterTest.java deleted file mode 100644 index dc2eafea11..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ODataPrimitiveAdapterTest.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.Month; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.UUID; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.datamodel.odata.helper.VdmEntity; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -class ODataPrimitiveAdapterTest -{ - private static final String primitivesEntityInput = """ - { - "EdmBinaryProperty" : "Rk9PIEJBUg==", - "EdmBooleanProperty" : true, - "EdmByteProperty" : 200, - "EdmDateTimeProperty" : "/Date(346833000)/", - "EdmDateTimeOffsetProperty" : "/Date(346833000-300)/", - "EdmDecimalProperty" : 9.223372036854776E18, - "EdmDoubleProperty" : 3.1415926535897E93, - "EdmGuidProperty" : "00000000-1111-2222-3333-444444444444", - "EdmInt16Property" : 1980, - "EdmInt32Property" : 16777216, - "EdmInt64Property" : 9223372036854775800, - "EdmSByteProperty" : -120, - "EdmSingleProperty" : 3.14, - "EdmStringProperty" : "TEST STRING", - "EdmTimeProperty" : "PT07H30M00S" - } - """; - - private static final String expectedSerializedEntity = """ - {\ - "versionIdentifier":null,\ - "EdmBinaryProperty":"Rk9PIEJBUg==",\ - "EdmBooleanProperty":true,\ - "EdmByteProperty":200,\ - "EdmDateTimeProperty":"/Date(1649342635567)/",\ - "EdmDateTimeOffsetProperty":"/Date(1649342635567-0300)/",\ - "EdmDecimalProperty":9.223372036854776E+18,\ - "EdmDoubleProperty":3.1415926535897E93,\ - "EdmGuidProperty":"00000000-1111-2222-3333-444444444444",\ - "EdmInt16Property":1980,\ - "EdmInt32Property":16777216,\ - "EdmInt64Property":9223372036854775800,\ - "EdmSByteProperty":-120,\ - "EdmSingleProperty":3.14,\ - "EdmStringProperty":"TEST STRING",\ - "EdmTimeProperty":"PT14H25M34.567S"\ - }\ - """; - - @Builder - @Data - @NoArgsConstructor - @AllArgsConstructor - @ToString( doNotUseGetters = true, callSuper = true ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataVdmEntityAdapterFactory.class ) - public static class PrimitivesEntity extends VdmEntity - { - @SerializedName( "EdmBinaryProperty" ) - @JsonProperty( "EdmBinaryProperty" ) - @Nullable - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataBinaryAdapter.class ) - private byte[] edmBinaryProperty; - - @SerializedName( "EdmBooleanProperty" ) - @JsonProperty( "EdmBooleanProperty" ) - @Nullable - @JsonAdapter( com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataBooleanAdapter.class ) - private Boolean edmBooleanProperty; - - @SerializedName( "EdmByteProperty" ) - @JsonProperty( "EdmByteProperty" ) - @Nullable - private Short edmByteProperty; - - @SerializedName( "EdmDateTimeProperty" ) - @JsonProperty( "EdmDateTimeProperty" ) - @Nullable - @JsonAdapter( LocalDateTimeAdapter.class ) - @JsonSerialize( using = JacksonLocalDateTimeSerializer.class ) - @JsonDeserialize( using = JacksonLocalDateTimeDeserializer.class ) - private LocalDateTime edmDateTimeProperty; - - @SerializedName( "EdmDateTimeOffsetProperty" ) - @JsonProperty( "EdmDateTimeOffsetProperty" ) - @Nullable - @JsonAdapter( ZonedDateTimeAdapter.class ) - @JsonSerialize( using = JacksonZonedDateTimeSerializer.class ) - @JsonDeserialize( using = JacksonZonedDateTimeDeserializer.class ) - private ZonedDateTime edmDateTimeOffsetProperty; - - @SerializedName( "EdmDecimalProperty" ) - @JsonProperty( "EdmDecimalProperty" ) - @Nullable - private BigDecimal edmDecimalProperty; - - @SerializedName( "EdmDoubleProperty" ) - @JsonProperty( "EdmDoubleProperty" ) - @Nullable - private Double edmDoubleProperty; - - @SerializedName( "EdmGuidProperty" ) - @JsonProperty( "EdmGuidProperty" ) - @Nullable - private UUID edmGuidProperty; - - @SerializedName( "EdmInt16Property" ) - @JsonProperty( "EdmInt16Property" ) - @Nullable - private Short edmInt16Property; - - @SerializedName( "EdmInt32Property" ) - @JsonProperty( "EdmInt32Property" ) - @Nullable - private Integer edmInt32Property; - - @SerializedName( "EdmInt64Property" ) - @JsonProperty( "EdmInt64Property" ) - @Nullable - private Long edmInt64Property; - - @SerializedName( "EdmSByteProperty" ) - @JsonProperty( "EdmSByteProperty" ) - @Nullable - private Byte edmSByteProperty; - - @SerializedName( "EdmSingleProperty" ) - @JsonProperty( "EdmSingleProperty" ) - @Nullable - private Float edmSingleProperty; - - @SerializedName( "EdmStringProperty" ) - @JsonProperty( "EdmStringProperty" ) - @Nullable - private String edmStringProperty; - - @SerializedName( "EdmTimeProperty" ) - @JsonProperty( "EdmTimeProperty" ) - @Nullable - @JsonSerialize( using = JacksonLocalTimeSerializer.class ) - @JsonDeserialize( using = JacksonLocalTimeDeserializer.class ) - @JsonAdapter( LocalTimeAdapter.class ) - private LocalTime edmTimeProperty; - - @Getter - @Setter - @Builder.Default - private transient String servicePath = "NOT_APPLICABLE"; - - @Override - protected String getEntityCollection() - { - return "PrimitivesCollection"; - } - - @Nonnull - @Override - public Class getType() - { - return PrimitivesEntity.class; - } - } - - @Test - void testGsonDeserialization() - { - final PrimitivesEntity primitivesEntity = new Gson().fromJson(primitivesEntityInput, PrimitivesEntity.class); - - assertThat(primitivesEntity).isNotNull(); - - assertThat(primitivesEntity.getEdmBinaryProperty()).containsExactly('F', 'O', 'O', ' ', 'B', 'A', 'R'); - assertThat(primitivesEntity.getEdmBooleanProperty()).isTrue(); - assertThat(primitivesEntity.getEdmByteProperty()).isEqualTo((short) 200); - - assertThat(primitivesEntity.getEdmDecimalProperty()).isEqualTo(new BigDecimal("9.223372036854776E18")); - assertThat(primitivesEntity.getEdmDoubleProperty()).isEqualTo(3.1415926535897E+93d); - assertThat(primitivesEntity.getEdmGuidProperty()) - .isEqualTo(UUID.fromString("00000000-1111-2222-3333-444444444444")); - assertThat(primitivesEntity.getEdmInt16Property()).isEqualTo((short) 1980); - assertThat(primitivesEntity.getEdmInt32Property()).isEqualTo(16777216); - assertThat(primitivesEntity.getEdmInt64Property()).isEqualTo(9223372036854775800L); - assertThat(primitivesEntity.getEdmSByteProperty()).isEqualTo((byte) -120); - assertThat(primitivesEntity.getEdmSingleProperty()).isEqualTo(3.14f); - assertThat(primitivesEntity.getEdmStringProperty()).isEqualTo("TEST STRING"); - assertThat(primitivesEntity.getEdmTimeProperty()).isEqualTo("07:30"); - } - - @Test - void testGsonSerialization() - throws Exception - { - final LocalDateTime expectedEdmDateTimeProperty = - LocalDateTime.of(2022, Month.APRIL, 7, 14, 43, 55, 567 * 1000000); - - final ZonedDateTime expectedEdmDateTimeOffsetProperty = - expectedEdmDateTimeProperty.atZone(ZoneId.of("GMT-05:00")); - - final LocalTime expectedEdmTimeProperty = LocalTime.of(14, 25, 34, 567 * 1000000); - - final PrimitivesEntity primitivesEntity = - PrimitivesEntity - .builder() - .edmBinaryProperty(new byte[] { 'F', 'O', 'O', ' ', 'B', 'A', 'R' }) - .edmBooleanProperty(true) - .edmByteProperty((short) 200) - .edmDateTimeProperty(expectedEdmDateTimeProperty) - .edmDateTimeOffsetProperty(expectedEdmDateTimeOffsetProperty) - .edmDecimalProperty(new BigDecimal("9.223372036854776E18")) - .edmDoubleProperty(3.1415926535897E93) - .edmGuidProperty(UUID.fromString("00000000-1111-2222-3333-444444444444")) - .edmInt16Property((short) 1980) - .edmInt32Property(16777216) - .edmInt64Property(9223372036854775800L) - .edmSByteProperty((byte) -120) - .edmSingleProperty(3.14f) - .edmStringProperty("TEST STRING") - .edmTimeProperty(expectedEdmTimeProperty) - .build(); - - // test gson - final GsonBuilder gsonBuilder = new GsonBuilder().serializeNulls().disableHtmlEscaping(); - final String actualGsonSerializedEntity = gsonBuilder.create().toJson(primitivesEntity); - assertThat(actualGsonSerializedEntity).isEqualTo(expectedSerializedEntity); - - // test jackson - final ObjectMapper jacksonMapper = new ObjectMapper(); - final String actualJacksonSerializedEntity = jacksonMapper.writeValueAsString(primitivesEntity); - assertThat(actualJacksonSerializedEntity).isEqualTo(expectedSerializedEntity); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ZonedDateTimeAdapterTest.java b/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ZonedDateTimeAdapterTest.java deleted file mode 100644 index 342577a681..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/s4hana/datamodel/odata/adapter/ZonedDateTimeAdapterTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.sap.cloud.sdk.s4hana.datamodel.odata.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Month; -import java.time.ZoneId; -import java.time.ZonedDateTime; - -import org.junit.jupiter.api.Test; - -import com.google.gson.TypeAdapter; - -import lombok.SneakyThrows; - -class ZonedDateTimeAdapterTest -{ - @Test - @SneakyThrows - void standardFormatCases() - { - final TypeAdapter adapter = new ZonedDateTimeAdapter(); - - assertThat(adapter.fromJson("\"/Date(1649669575192)/\"")).isEqualTo("2022-04-11T09:32:55.192Z"); - assertThat(adapter.fromJson("\"/Date(1649669575192+0120)/\"")).isEqualTo("2022-04-11T09:32:55.192+02:00"); - } - - @Test - @SneakyThrows - void declaredOffsetTooLargeReturnsNull() - { - final TypeAdapter adapter = new ZonedDateTimeAdapter(); - - assertThat(adapter.fromJson("\"/Date(1649669575192+1441)/\"")).isNull(); - } - - @Test - @SneakyThrows - void invalidPatternReturnsNull() - { - final TypeAdapter adapter = new ZonedDateTimeAdapter(); - - assertThat(adapter.fromJson("\"Something that is not a date\"")).isNull(); - } - - @Test - @SneakyThrows - void isoPatternReturnsNull() - { - final TypeAdapter adapter = new ZonedDateTimeAdapter(); - - assertThat(adapter.fromJson("\"2022-04-11T12:47:14.1234567-02:30\"")).isNull(); - assertThat(adapter.fromJson("\"2022-04-11T12:47:14.1234567+4:30\"")).isNull(); - assertThat(adapter.fromJson("\"2022-04-11T12:47:14.1234567Z\"")).isNull(); - assertThat(adapter.fromJson("\"2022-04-11T12:47:14\"")).isNull(); - assertThat(adapter.fromJson("\"2022-04-11T12:47\"")).isNull(); - assertThat(adapter.fromJson("\"2-4-1T2:7\"")).isNull(); - } - - @Test - @SneakyThrows - void excessivelyLongNumberReturnsNull() - { - final TypeAdapter adapter = new ZonedDateTimeAdapter(); - - assertThat(adapter.fromJson("\"/Date(100000000000000000000000)/\"")).isNull(); - } - - @Test - @SneakyThrows - void nonStringValueReturnsNull() - { - final TypeAdapter adapter = new ZonedDateTimeAdapter(); - - assertThat(adapter.fromJson("1234")).isNull(); - } - - @Test - void writeZonedDateTime() - { - final TypeAdapter adapter = new ZonedDateTimeAdapter(); - - assertThat( - adapter.toJson(ZonedDateTime.of(2022, Month.APRIL.getValue(), 11, 11, 36, 55, 123000000, ZoneId.of("GMT")))) - .isEqualTo("\"/Date(1649677015123)/\""); - assertThat( - adapter - .toJson(ZonedDateTime.of(2022, Month.APRIL.getValue(), 11, 11, 36, 55, 123000000, ZoneId.of("GMT+2")))) - .isEqualTo("\"/Date(1649677015123+0120)/\""); - } -} diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchRequestFunctionImportWithGet.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchRequestFunctionImportWithGet.txt deleted file mode 100644 index 32772d9ba7..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchRequestFunctionImportWithGet.txt +++ /dev/null @@ -1,10 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -GET awesomeFunction?FirstName='John'&LastName='Doe' HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchRequestFunctionImportWithPost.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchRequestFunctionImportWithPost.txt deleted file mode 100644 index 436ae71527..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchRequestFunctionImportWithPost.txt +++ /dev/null @@ -1,17 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000002 - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -POST awesomeFunction?FirstName='John'&LastName='Doe' HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{} - ---changeset_00000000-0000-0000-0000-000000000002-- - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchRequestFunctionImportWithPostWithCustomHeader.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchRequestFunctionImportWithPostWithCustomHeader.txt deleted file mode 100644 index 7c8d4ab875..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchRequestFunctionImportWithPostWithCustomHeader.txt +++ /dev/null @@ -1,18 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000002 - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -POST awesomeFunction?FirstName='John'&LastName='Doe' HTTP/1.1 -Accept: application/json -Content-Type: application/json -foo: bar - -{} - ---changeset_00000000-0000-0000-0000-000000000002-- - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchResponse.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchResponse.txt deleted file mode 100644 index cb7449970a..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchFunctionImportTest/BatchResponse.txt +++ /dev/null @@ -1,90 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"results":[{"StringValue":"Foo","IntegerValue":42}]}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 201 Created -Location: https://localhost/service/TestEntities('new') -Content-Type: application/json - -{"d":{"IntegerValue":1337}} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4-- - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"StringValue":"Foo","IntegerValue":9000}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: text/plain - -{"value":42} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 204 No response. -Content-Type: application/json - -{"value":["Something","here"]} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000-- ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 400 Bad Request -Content-Type: application/json;charset=utf-8 -Content-Length: 1050 -dataserviceversion: 1.0 - -{"error":{"code":"ZCU/100","message":{"lang":"nl","value":"Service 0000000003 0000000010 niet gevonden voor operatie 0410"},"innererror":{"application":{"component_id":"","service_namespace":"/SAP/","service_id":"ZCU_PE_ORDER_SRV","service_version":"0001"},"transactionid":"23F2932D54040110E005FD84A23B406E","timestamp":"20201215151501.0634490","Error_Resolution":{"SAP_Transaction":"Run transaction /IWFND/ERROR_LOG on SAP Gateway hub system (System Alias ) and search for entries with the timestamp above for more details","SAP_Note":"See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)","Batch_SAP_Note":"See SAP Note 1869434 for details about working with $batch (https://service.sap.com/sap/support/notes/1869434)"},"errordetails":[{"code":"ZCU/100","message":"Service 0000000003 0000000010 niet gevonden voor operatie 0410","propertyref":"","severity":"error","target":""},{"code":"/IWBEP/CX_MGW_BUSI_EXCEPTION","message":"Fout bij wijzigen PE order.","propertyref":"","severity":"error","target":""}]}}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"awesomeFunction":"awesomeStuff"}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"awesomeFunction":["foo","bar"]}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"IntegerValue":33,"StringValue":"Alice"}}} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchRequestUnitTest/BatchRequest.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchRequestUnitTest/BatchRequest.txt deleted file mode 100644 index 7a25853f38..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchRequestUnitTest/BatchRequest.txt +++ /dev/null @@ -1,83 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -GET Entities?$select=StringValue,IntegerValue&$top=10 HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000002 - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -POST Entities HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"IntegerValue":12} - ---changeset_00000000-0000-0000-0000-000000000002-- - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -GET Entities(9000) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000003 - ---changeset_00000000-0000-0000-0000-000000000003 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 4 - -PATCH Entities(13) HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{} - ---changeset_00000000-0000-0000-0000-000000000003 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 5 - -DELETE Entities(14) HTTP/1.1 -Accept: application/json - - ---changeset_00000000-0000-0000-0000-000000000003-- - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000004 - ---changeset_00000000-0000-0000-0000-000000000004 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 6 - -DELETE Entities(15) HTTP/1.1 -Accept: application/json - - ---changeset_00000000-0000-0000-0000-000000000004 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 7 - -DELETE Entities(16) HTTP/1.1 -Accept: application/json - - ---changeset_00000000-0000-0000-0000-000000000004-- - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchRequestUnitTest/BatchRequestWithCustomHeaders.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchRequestUnitTest/BatchRequestWithCustomHeaders.txt deleted file mode 100644 index 4864bce1eb..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchRequestUnitTest/BatchRequestWithCustomHeaders.txt +++ /dev/null @@ -1,90 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -GET Entities?$select=StringValue,IntegerValue&$top=10 HTTP/1.1 -Accept: application/json -header-read_all: read_all - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000002 - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -POST Entities HTTP/1.1 -Accept: application/json -Content-Type: application/json -header-create: create - -{"IntegerValue":12} - ---changeset_00000000-0000-0000-0000-000000000002-- - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -GET Entities(9000) HTTP/1.1 -Accept: application/json -header-read_by_key: read_by_key - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000003 - ---changeset_00000000-0000-0000-0000-000000000003 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 4 - -PATCH Entities(13) HTTP/1.1 -Accept: application/json -Content-Type: application/json -header-update: update - -{} - ---changeset_00000000-0000-0000-0000-000000000003 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 5 - -DELETE Entities(14) HTTP/1.1 -Accept: application/json -header-delete: delete-entity14 - - ---changeset_00000000-0000-0000-0000-000000000003-- - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000004 - ---changeset_00000000-0000-0000-0000-000000000004 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 6 - -DELETE Entities(15) HTTP/1.1 -Accept: application/json -header-delete: delete-entity15 - - ---changeset_00000000-0000-0000-0000-000000000004 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 7 - -DELETE Entities(16) HTTP/1.1 -Accept: application/json -header-delete: delete-entity16 - - ---changeset_00000000-0000-0000-0000-000000000004-- - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchRequestUnitTest/BatchResponse.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchRequestUnitTest/BatchResponse.txt deleted file mode 100644 index 68718fe227..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODataV2BatchRequestUnitTest/BatchResponse.txt +++ /dev/null @@ -1,63 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"results":[{"StringValue":"Foo","IntegerValue":42}]}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 201 Created -Location: https://localhost/service/TestEntities('new') -Content-Type: application/json - -{"d":{"IntegerValue":1337}} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4-- - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"StringValue":"Foo","IntegerValue":9000}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: text/plain - -{"value":42} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 204 No response. -Content-Type: application/json - -{"value":["Something","here"]} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000-- ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 400 Bad Request -Content-Type: application/json;charset=utf-8 -Content-Length: 1050 -dataserviceversion: 1.0 - -{"error":{"code":"ZCU/100","message":{"lang":"nl","value":"Service 0000000003 0000000010 niet gevonden voor operatie 0410"},"innererror":{"application":{"component_id":"","service_namespace":"/SAP/","service_id":"ZCU_PE_ORDER_SRV","service_version":"0001"},"transactionid":"23F2932D54040110E005FD84A23B406E","timestamp":"20201215151501.0634490","Error_Resolution":{"SAP_Transaction":"Run transaction /IWFND/ERROR_LOG on SAP Gateway hub system (System Alias ) and search for entries with the timestamp above for more details","SAP_Note":"See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)","Batch_SAP_Note":"See SAP Note 1869434 for details about working with $batch (https://service.sap.com/sap/support/notes/1869434)"},"errordetails":[{"code":"ZCU/100","message":"Service 0000000003 0000000010 niet gevonden voor operatie 0410","propertyref":"","severity":"error","target":""},{"code":"/IWBEP/CX_MGW_BUSI_EXCEPTION","message":"Fout bij wijzigen PE order.","propertyref":"","severity":"error","target":""}]}}} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODatav2BatchConnectionTest/BatchResponseWithChangeset.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODatav2BatchConnectionTest/BatchResponseWithChangeset.txt deleted file mode 100644 index 040aa489ca..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODatav2BatchConnectionTest/BatchResponseWithChangeset.txt +++ /dev/null @@ -1,62 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"results":[{"StringValue":"Foo","IntegerValue":42}]}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 201 Created -Location: https://localhost/service/TestEntities('new') -Content-Type: application/json - -{"d":{"IntegerValue":1337}} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4-- - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"StringValue":"Foo","IntegerValue":9000}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: text/plain - -{"value":42} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 204 No response. -Content-Type: application/json - -{"value":["Something","here"]} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 201 Created -Location: https://localhost/service/TestEntities('new') -Content-Type: application/json - -{"d":{"IntegerValue":1447}} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000-- ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODatav2BatchConnectionTest/BatchResponseWithError.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODatav2BatchConnectionTest/BatchResponseWithError.txt deleted file mode 100644 index 7da32711f4..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODatav2BatchConnectionTest/BatchResponseWithError.txt +++ /dev/null @@ -1,29 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"results":[{"StringValue":"Foo","IntegerValue":42}]}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"StringValue":"Foo","IntegerValue":9000}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 400 Bad Request -Content-Type: application/json;charset=utf-8 -Content-Length: 1050 -dataserviceversion: 1.0 - -{"error":{"code":"ZCU/100","message":{"lang":"nl","value":"Service 0000000003 0000000010 niet gevonden voor operatie 0410"},"innererror":{"application":{"component_id":"","service_namespace":"/SAP/","service_id":"ZCU_PE_ORDER_SRV","service_version":"0001"},"transactionid":"23F2932D54040110E005FD84A23B406E","timestamp":"20201215151501.0634490","Error_Resolution":{"SAP_Transaction":"Run transaction /IWFND/ERROR_LOG on SAP Gateway hub system (System Alias ) and search for entries with the timestamp above for more details","SAP_Note":"See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)","Batch_SAP_Note":"See SAP Note 1869434 for details about working with $batch (https://service.sap.com/sap/support/notes/1869434)"},"errordetails":[{"code":"ZCU/100","message":"Service 0000000003 0000000010 niet gevonden voor operatie 0410","propertyref":"","severity":"error","target":""},{"code":"/IWBEP/CX_MGW_BUSI_EXCEPTION","message":"Fout bij wijzigen PE order.","propertyref":"","severity":"error","target":""}]}}} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- diff --git a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODatav2BatchConnectionTest/BatchResponseWithoutChangeset.txt b/datamodel/odata-core-apache-httpclient5/src/test/resources/ODatav2BatchConnectionTest/BatchResponseWithoutChangeset.txt deleted file mode 100644 index cca2d42f9b..0000000000 --- a/datamodel/odata-core-apache-httpclient5/src/test/resources/ODatav2BatchConnectionTest/BatchResponseWithoutChangeset.txt +++ /dev/null @@ -1,19 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"results":[{"StringValue":"Foo","IntegerValue":42}]}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"d":{"StringValue":"Foo","IntegerValue":9000}} - ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/pom.xml b/datamodel/odata-v4-core-apache-httpclient5/pom.xml deleted file mode 100644 index 9f3e5a41d5..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/pom.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - 4.0.0 - - com.sap.cloud.sdk.datamodel - datamodel-parent - 5.35.0-SNAPSHOT - - odata-v4-core-apache-httpclient5 - jar - Data Model - OData V4 Services - Core (HttpClient 5) - OData V4 Services data model (VDM) - core classes using Apache HttpClient 5. - https://sap.github.io/cloud-sdk/docs/java/getting-started - - SAP SE - https://www.sap.com - - - - The Apache Software License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - SAP - cloudsdk@sap.com - SAP SE - https://www.sap.com - - - - false - true - - - - com.sap.cloud.sdk.datamodel - odata-client-apache-httpclient5 - ${project.version} - - - com.sap.cloud.sdk.cloudplatform - cloudplatform-core - - - com.sap.cloud.sdk.cloudplatform - cloudplatform-connectivity - - - com.sap.cloud.sdk.datamodel - fluent-result - - - org.slf4j - slf4j-api - - - com.google.guava - guava - - - com.google.code.gson - gson - - - com.fasterxml.jackson.core - jackson-annotations - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - - - org.apache.httpcomponents.core5 - httpcore5 - - - org.apache.httpcomponents.client5 - httpclient5 - - - io.vavr - vavr - - - - org.projectlombok - lombok - provided - - - - com.sap.cloud.sdk.cloudplatform - connectivity-apache-httpclient5 - test - - - com.sap.cloud.sdk - testutil - test - - - org.assertj - assertj-core - test - - - org.skyscreamer - jsonassert - test - - - org.mockito - mockito-core - test - - - org.wiremock - wiremock - test - - - org.junit.jupiter - junit-jupiter-api - test - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - commons-beanutils:commons-beanutils - - - - - - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/CustomBigDecimalTypeAdapter.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/CustomBigDecimalTypeAdapter.java deleted file mode 100644 index eeb7f604b8..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/CustomBigDecimalTypeAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; -import java.math.BigDecimal; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.JsonSyntaxException; -import com.google.gson.TypeAdapter; -import com.google.gson.internal.LazilyParsedNumber; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -/** - * Custom type adapter for BigDecimal that doesn't serialise BigDecimal into it's scientific notation. For e.g. if the - * value of the attribute is 0.000000002, using this serializer ensures that it doesn't get converted to 2E-9. - */ -class CustomBigDecimalTypeAdapter extends TypeAdapter -{ - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final BigDecimal value ) - throws IOException - { - if( value == null ) { - out.nullValue(); - } else { - final Number number = new LazilyParsedNumber(value.toPlainString()); - out.value(number); - } - } - - @Override - @Nullable - public BigDecimal read( @Nonnull final JsonReader in ) - throws IOException - { - if( in.peek() == JsonToken.NULL ) { - in.nextNull(); - return null; - } - try { - return new BigDecimal(in.nextString()); - } - catch( final NumberFormatException e ) { - throw new JsonSyntaxException("The string could not be parsed as decimal.", e); - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonCustomFieldAdapter.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonCustomFieldAdapter.java deleted file mode 100644 index 76f4ac1de5..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonCustomFieldAdapter.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.JsonPrimitive; -import com.google.gson.TypeAdapter; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -import lombok.RequiredArgsConstructor; - -/** - * For internal use only by data model classes. - */ -@RequiredArgsConstructor -public class GsonCustomFieldAdapter extends TypeAdapter -{ - private static final Type MAP_TYPE = new TypeToken>() - { - }.getType(); - - @Nonnull - private final Gson gson; - - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final Object value ) - { - if( value instanceof String ) { - gson.toJson(new JsonPrimitive((String) value), out); - } - if( value instanceof Number ) { - gson.toJson(new JsonPrimitive((Number) value), out); - } - if( value instanceof Boolean ) { - gson.toJson(new JsonPrimitive((Boolean) value), out); - } - gson.toJson(value, MAP_TYPE, out); - } - - @Override - @Nullable - public Object read( @Nonnull final JsonReader in ) - throws IOException - { - if( JsonToken.STRING == in.peek() ) { - return in.nextString(); - } - if( JsonToken.NUMBER == in.peek() ) { - return in.nextDouble(); - } - if( JsonToken.BOOLEAN == in.peek() ) { - return in.nextBoolean(); - } - return gson.fromJson(in, MAP_TYPE); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonODataConverterAdapter.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonODataConverterAdapter.java deleted file mode 100644 index 71344b6657..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonODataConverterAdapter.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@RequiredArgsConstructor( staticName = "of" ) -class GsonODataConverterAdapter extends TypeAdapter -{ - private final AbstractTypeConverter converter; - - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final JavaT value ) - throws IOException - { - if( value == null ) { - out.nullValue(); - } else { - final ConvertedObject jsonValue = converter.toDomain(value); - if( jsonValue.isNotConvertible() ) { - log.warn("Not serializable: {}", value); - } else { - out.value(jsonValue.get()); - } - } - } - - @Override - @Nullable - public JavaT read( @Nonnull final JsonReader in ) - throws IOException - { - if( in.peek() == JsonToken.NULL ) { - in.nextNull(); - return null; - } - final String value = in.nextString(); - final ConvertedObject convertedObject = converter.fromDomain(value); - if( convertedObject.isNotConvertible() ) { - log.warn("Not deserializable: {}", value); - } - return convertedObject.orNull(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmAdapterFactory.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmAdapterFactory.java deleted file mode 100644 index cc6bd7ef54..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmAdapterFactory.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.math.BigDecimal; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmObject; - -import lombok.extern.slf4j.Slf4j; - -/** - * General purpose VDM adapter factory. - */ -@Slf4j -public class GsonVdmAdapterFactory implements TypeAdapterFactory -{ - @SuppressWarnings( "unchecked" ) - @Override - @Nullable - public TypeAdapter create( @Nonnull final Gson gson, @Nonnull final TypeToken type ) - { - final Class rawType = type.getRawType(); - if( VdmEnum.class.isAssignableFrom(rawType) ) { - return (TypeAdapter) new GsonVdmEnumAdapter<>((Class) rawType); - } - if( VdmObject.class.isAssignableFrom(rawType) ) { - return (TypeAdapter) new GsonVdmEntityAdapter<>(this, gson, rawType); - } - if( BigDecimal.class.isAssignableFrom(rawType) ) { - return (TypeAdapter) new CustomBigDecimalTypeAdapter(); - } - - for( final ODataGenericConverter converter : ODataGenericConverter.DEFAULT_CONVERTERS ) { - if( converter.getType().isAssignableFrom(rawType) ) { - return (TypeAdapter) GsonODataConverterAdapter.of(converter); - } - } - log.trace("Could not find custom type adapter for type {}.", rawType); - return null; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmEntityAdapter.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmEntityAdapter.java deleted file mode 100644 index e256f1bbf3..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmEntityAdapter.java +++ /dev/null @@ -1,350 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmObject; -import com.sap.cloud.sdk.result.ElementName; - -import io.vavr.control.Option; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * For internal use only by data model classes - * - * @param - * The entity type. - */ -@Slf4j -public class GsonVdmEntityAdapter extends TypeAdapter> -{ - @Nonnull - private final Gson gson; - @Nonnull - private final Class entityRawType; - @Nonnull - private final TypeAdapterFactory adapterFactory; - - @Nullable - private TypeAdapter> delegateAdapter = null; - @Nullable - private GsonVdmEntityAdapter superClassAdapter = null; - - @Nonnull - private final Map entityProperties; - @Nonnull - private final TypeAdapter customFieldAdapter; - - @AllArgsConstructor - private static class PropertySerializationInfo - { - @Getter - private final Field javaField; - @Getter - private final TypeAdapter fieldAdapter; - } - - private TypeAdapter getAdapterFromField( final Field entityField, final Gson gson ) - { - if( entityField.isAnnotationPresent(JsonAdapter.class) ) { - try { - return (TypeAdapter) entityField - .getAnnotation(JsonAdapter.class) - .value() - .getDeclaredConstructor() - .newInstance(); - } - catch( final - InstantiationException - | IllegalAccessException - | NoSuchMethodException - | InvocationTargetException e ) { - log.warn("Could not instantiate the field '" + entityField.getName() + "'.", e); - } - } - if( Iterable.class.isAssignableFrom(entityField.getType()) ) { - final ParameterizedType entityFieldTypeParams = (ParameterizedType) entityField.getGenericType(); - final Type listEntityType = entityFieldTypeParams.getActualTypeArguments()[0]; - final TypeAdapter innerTypeAdapter = gson.getAdapter(TypeToken.get(listEntityType)); - return new GsonVdmEntityListAdapter<>(gson, innerTypeAdapter); - } - final TypeAdapter fieldAdapter = adapterFactory.create(gson, TypeToken.get(entityField.getType())); - if( fieldAdapter != null ) { - return fieldAdapter; - } - - return gson.getAdapter(entityField.getType()); - } - - /** - * For internal use only by data model classes. - * - * @param adapterFactory - * The adapter type factory. - * @param gson - * The GSON instance. - * @param entityRawType - * The entity type reference. - */ - @SuppressWarnings( "unchecked" ) - public GsonVdmEntityAdapter( - @Nonnull final TypeAdapterFactory adapterFactory, - @Nonnull final Gson gson, - @Nonnull final Class entityRawType ) - { - this.gson = gson; - this.entityRawType = entityRawType; - this.adapterFactory = adapterFactory; - - entityProperties = new LinkedHashMap<>(); - for( final Field entityField : entityRawType.getDeclaredFields() ) { - if( entityField.isAnnotationPresent(ElementName.class) - || entityField.isAnnotationPresent(SerializedName.class) ) { - - TypeAdapter fieldAdapter = null; - - // Don't get the adapter yet if the field is a navigation property. - // Otherwise an infinite loop can happen if there are circular navigation properties. - if( !VdmObject.class.isAssignableFrom(entityField.getType()) - && !Iterable.class.isAssignableFrom(entityField.getType()) ) { - - fieldAdapter = getAdapterFromField(entityField, gson); - } - - final String entityFieldKey = - entityField.isAnnotationPresent(ElementName.class) - ? entityField.getAnnotation(ElementName.class).value() - : entityField.getAnnotation(SerializedName.class).value(); - - entityProperties.put(entityFieldKey, new PropertySerializationInfo(entityField, fieldAdapter)); - } - } - - customFieldAdapter = new GsonCustomFieldAdapter(gson); - - final Class entityRawSuperType = entityRawType.getSuperclass(); - if( Object.class == entityRawSuperType ) { - delegateAdapter = - (TypeAdapter>) gson.getDelegateAdapter(adapterFactory, TypeToken.get(entityRawType)); - } else { - superClassAdapter = new GsonVdmEntityAdapter<>(adapterFactory, gson, entityRawSuperType); - } - } - - /** - * For internal use only by data model classes. - * - * @param jsonReader - * The JsonReader reference. - * @return The deserialized entity instance. - * @throws IOException - * When deserialization failed. - */ - @Override - @Nullable - public VdmObject read( @Nonnull final JsonReader jsonReader ) - throws IOException - { - try { - @SuppressWarnings( "unchecked" ) - final VdmObject entity = (VdmObject) entityRawType.getDeclaredConstructor().newInstance(); - - if( jsonReader.peek() == JsonToken.BEGIN_OBJECT ) { - jsonReader.beginObject(); - - while( jsonReader.hasNext() ) { - final String propertyKey = jsonReader.nextName(); - - if( "__metadata".equals(propertyKey) ) { - jsonReader.skipValue(); - } else if( "__deferred".equals(propertyKey) ) { - jsonReader.skipValue(); - jsonReader.endObject(); - return null; - } else { - final PropertySerializationInfo propertyInfo = getPropertySerializationInfo(propertyKey); - if( propertyInfo != null ) { - final Field entityField = propertyInfo.getJavaField(); - TypeAdapter fieldAdapter = propertyInfo.getFieldAdapter(); - - if( fieldAdapter == null ) { - fieldAdapter = getAdapterFromField(entityField, gson); - } - - if( fieldAdapter != null ) { - final Object attributeValue = fieldAdapter.read(jsonReader); - - // To be safe/secure, since fields are declared private in the VDM. - final boolean oldAccessibleValue = entityField.canAccess(entity); - entityField.setAccessible(true); - entityField.set(entity, attributeValue); - entityField.setAccessible(oldAccessibleValue); - } - } else { - final Object customValue = customFieldAdapter.read(jsonReader); - handleCustomField(entity, propertyKey, customValue); - } - } - } - - jsonReader.endObject(); - } else if( jsonReader.peek() == JsonToken.NULL ) { - jsonReader.nextNull(); - return null; - } - - return entity; - } - catch( final - InstantiationException - | IllegalAccessException - | NoSuchMethodException - | InvocationTargetException e ) { - log - .error( - "Could not instantiate or initialize '" - + entityRawType.getName() - + "'. " - + "Returning null instead.", - e); - } - return null; - } - - private void handleCustomField( final VdmObject object, final String name, final Object value ) - { - if( Arrays.asList(VdmObject.ODATA_TYPE_ANNOTATIONS).contains(name) ) { - // do nothing - return; - } - if( object instanceof VdmEntity && Arrays.asList(VdmObject.ODATA_VERSION_ANNOTATIONS).contains(name) ) { - ((VdmEntity) object).setVersionIdentifier(value.toString()); - return; - } - object.setCustomField(name, value); - } - - @Nullable - private PropertySerializationInfo getPropertySerializationInfo( final String propertyKey ) - { - if( entityProperties.containsKey(propertyKey) ) { - return entityProperties.get(propertyKey); - } - if( superClassAdapter != null ) { - return superClassAdapter.getPropertySerializationInfo(propertyKey); - } - return null; - } - - /** - * For internal use only by data model classes. - * - * @param out - * The JsonWriter reference. - * @param value - * The entity instance to be serialized. - * @throws IOException - * When serialization failed. - */ - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final VdmObject value ) - throws IOException - { - if( value != null ) { - final JsonObject entityAsJson = getEntityAsJsonObject(value); - final JsonObject customFieldsAsJson = gson.toJsonTree(value.getCustomFields()).getAsJsonObject(); - - for( final Map.Entry annotationProperty : value.getAnnotationProperties().entrySet() ) { - entityAsJson.add(annotationProperty.getKey(), gson.toJsonTree(annotationProperty.getValue())); - } - - for( final Map.Entry customField : customFieldsAsJson.entrySet() ) { - entityAsJson.add(customField.getKey(), customField.getValue()); - } - - gson.toJson(entityAsJson, out); - } else { - out.nullValue(); - } - } - - @SuppressWarnings( "unchecked" ) - private JsonObject getEntityAsJsonObject( final VdmObject value ) - { - if( delegateAdapter != null ) { - return delegateAdapter.toJsonTree(value).getAsJsonObject(); - } else { - final JsonObject entityAsJson = superClassAdapter.getEntityAsJsonObject(value); - for( final Map.Entry entityProperty : entityProperties.entrySet() ) { - try { - final PropertySerializationInfo serializationInfo = entityProperty.getValue(); - - // To be safe/secure, since fields are declared private in the VDM. - final Field propertyField = serializationInfo.getJavaField(); - final boolean oldAccessibleValue = propertyField.canAccess(value); - propertyField.setAccessible(true); - final Object propertyValue = propertyField.get(value); - propertyField.setAccessible(oldAccessibleValue); - - TypeAdapter fieldAdapter = (TypeAdapter) serializationInfo.getFieldAdapter(); - - if( fieldAdapter == null ) { - fieldAdapter = (TypeAdapter) getAdapterFromField(propertyField, gson); - } - - final JsonElement propertyValueAsJson = - (fieldAdapter != null) ? fieldAdapter.toJsonTree(propertyValue) : null; - - // Overwrites JSON property from the superclass if this class has a property with the same name. - entityAsJson.add(entityProperty.getKey(), propertyValueAsJson); - } - catch( final IllegalAccessException e ) { - log - .error( - "Could not serialize property '" - + entityProperty.getKey() - + "'. " - + "Returning null instead.", - e); - } - } - - // odata type - final String odataType = "#" + value.getOdataType(); - entityAsJson.addProperty(VdmObject.ODATA_TYPE_ANNOTATIONS[0], odataType); - - // odata version - if( value instanceof VdmEntity ) { - final Option versionIdentifier = ((VdmEntity) value).getVersionIdentifier(); - if( versionIdentifier.isDefined() ) { - entityAsJson.addProperty(VdmObject.ODATA_VERSION_ANNOTATIONS[0], versionIdentifier.get()); - } - } - - return entityAsJson; - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmEntityListAdapter.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmEntityListAdapter.java deleted file mode 100644 index a56b263c47..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmEntityListAdapter.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; - -/** - * For internal use only by data model classes. - * - * @param - * The entity type. - */ -public class GsonVdmEntityListAdapter extends TypeAdapter> -{ - @Nonnull - private final Gson gson; - @Nonnull - private final TypeAdapter entityAdapter; - - /** - * For internal use only by data model classes. - * - * @param gson - * The GSON instance. - * @param entityAdapter - * The entity adapter. - */ - public GsonVdmEntityListAdapter( @Nonnull final Gson gson, @Nonnull final TypeAdapter entityAdapter ) - { - this.gson = gson; - this.entityAdapter = entityAdapter; - } - - private List readArray( @Nonnull final JsonReader in ) - throws IOException - { - in.beginArray(); - final List entityList = new ArrayList<>(); - - while( in.hasNext() ) { - final T entity = entityAdapter.read(in); - entityList.add(entity); - } - - in.endArray(); - return entityList; - } - - /** - * For internal use only by data model classes. - * - * @param in - * The JsonReader reference. - * @return The deserialized List of entities. - * @throws IOException - * When deserialization failed. - */ - @Override - @Nullable - public List read( @Nonnull final JsonReader in ) - throws IOException - { - List entityList = null; - if( in.peek() == JsonToken.BEGIN_OBJECT ) { - in.beginObject(); - if( in.peek() == JsonToken.NAME ) { - final String resultsKey = in.nextName(); - if( "results".equals(resultsKey) && in.peek() == JsonToken.BEGIN_ARRAY ) { - entityList = readArray(in); - } else { - in.skipValue(); - } - } - in.endObject(); - } else if( in.peek() == JsonToken.BEGIN_ARRAY ) { - entityList = readArray(in); - } else { - in.skipValue(); - } - return entityList; - } - - /** - * For internal use only by data model classes. - * - * @param out - * The JsonWriter reference. - * @param entityList - * The list of entities. - * @throws IOException - * When serialization failed. - */ - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final List entityList ) - throws IOException - { - if( entityList != null ) { - final JsonArray entityListAsJson = new JsonArray(); - for( final T entity : entityList ) { - entityListAsJson.add(entityAdapter.toJsonTree(entity)); - } - gson.toJson(entityListAsJson, out); - } else { - out.nullValue(); - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmEnumAdapter.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmEnumAdapter.java deleted file mode 100644 index 54a8d8c67e..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/GsonVdmEnumAdapter.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; -import java.util.Arrays; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; - -/** - * The VdmEnum type adapter for serializing and deserializing JSON payload. - * - * @param - * The generic type for which the adapter is applied. - */ -class GsonVdmEnumAdapter extends TypeAdapter -{ - private final ImmutableMap nameEnumLookupMap; - - GsonVdmEnumAdapter( @Nonnull final Class enumType ) - { - final VdmEnum[] constants = enumType.getEnumConstants(); - if( constants == null ) { - throw new IllegalArgumentException("Enum type " + enumType.getSimpleName() + " has no enum constants."); - } - this.nameEnumLookupMap = Maps.uniqueIndex(Arrays.asList(constants), VdmEnum::getName); - } - - @Override - public void write( @Nonnull final JsonWriter out, @Nullable final T value ) - throws IOException - { - if( value == null ) { - out.nullValue(); - } else { - final String name = value.getName(); - out.value(name); - } - } - - @Override - @SuppressWarnings( "unchecked" ) - @Nullable - public T read( @Nonnull final JsonReader in ) - throws IOException - { - if( in.peek() == JsonToken.NULL ) { - in.nextNull(); - return null; - } - final String name = in.nextString(); - final VdmEnum value = nameEnumLookupMap.getOrDefault(name, null); - return (T) value; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmEnumDeserializer.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmEnumDeserializer.java deleted file mode 100644 index 4cebbc98ef..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmEnumDeserializer.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; -import java.util.Arrays; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; - -import io.vavr.control.Option; -import lombok.extern.slf4j.Slf4j; - -/** - * Jackson deserializer adapter for {@link VdmEnum} types. - */ -@Slf4j -public class JacksonVdmEnumDeserializer extends StdDeserializer implements ContextualDeserializer -{ - private static final long serialVersionUID = 3282694560867458205L; - private ImmutableMap nameEnumLookupMap = ImmutableMap.of(); - private Class valueType; - - /** - * Default constructor. - */ - protected JacksonVdmEnumDeserializer() - { - super((Class) null); - } - - @Override - @Nonnull - public - JacksonVdmEnumDeserializer - createContextual( @Nonnull final DeserializationContext ctxt, @Nullable final BeanProperty property ) - { - final JavaType valueType = - Option - .of(property) - .map(BeanProperty::getType) - .map(t -> t.containedType(0)) - .getOrElse(ctxt::getContextualType); - final JacksonVdmEnumDeserializer deserializer = new JacksonVdmEnumDeserializer(); - - @SuppressWarnings( "unchecked" ) - final Class enumType = (Class) valueType.getRawClass(); - final VdmEnum[] constants = enumType.getEnumConstants(); - if( constants == null ) { - throw new IllegalStateException("Enum type " + enumType.getSimpleName() + " has no enum constants."); - } - deserializer.valueType = enumType; - deserializer.nameEnumLookupMap = Maps.uniqueIndex(Arrays.asList(constants), VdmEnum::getName); - return deserializer; - } - - @Override - @Nullable - public VdmEnum deserialize( @Nonnull final JsonParser parser, @Nonnull final DeserializationContext ctxt ) - throws IOException - { - final JsonToken currentToken = parser.currentToken(); - if( currentToken == JsonToken.VALUE_NULL ) { - return null; - } - if( currentToken != JsonToken.VALUE_STRING ) { - throw new IOException( - String.format("Failed to deserialize enum type %s from token kind %s", valueType, currentToken)); - } - - final String serializedValue = parser.getValueAsString(); - return nameEnumLookupMap.getOrDefault(serializedValue, null); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmEnumSerializer.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmEnumSerializer.java deleted file mode 100644 index b37bad844a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmEnumSerializer.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; - -import lombok.extern.slf4j.Slf4j; - -/** - * Jackson serializer adapter for {@link VdmEnum} types. - */ -@Slf4j -public class JacksonVdmEnumSerializer extends StdSerializer implements ContextualSerializer -{ - private static final long serialVersionUID = -5794503059647076980L; - - /** - * Default constructor. - */ - protected JacksonVdmEnumSerializer() - { - super((Class) null); - } - - @Override - public void serialize( - @Nullable final VdmEnum object, - @Nonnull final JsonGenerator gen, - @Nonnull final SerializerProvider prov ) - throws IOException - { - if( object == null ) { - gen.writeNull(); - return; - } - final String enumName = object.getName(); - gen.writeString(enumName); - } - - @Override - @Nonnull - public - JsonSerializer - createContextual( @Nonnull final SerializerProvider prov, @Nullable final BeanProperty property ) - { - final JacksonVdmEnumSerializer serializer = new JacksonVdmEnumSerializer(); - return serializer; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmObjectDeserializer.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmObjectDeserializer.java deleted file mode 100644 index 27e1c12ac9..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmObjectDeserializer.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.deser.ContextualDeserializer; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.introspect.AnnotatedField; -import com.fasterxml.jackson.databind.introspect.AnnotationMap; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmObject; -import com.sap.cloud.sdk.result.ElementName; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -import io.vavr.control.Option; -import lombok.extern.slf4j.Slf4j; - -/** - * Jackson deserializer adapter for {@link VdmEntity} and {@link VdmObject} types. - */ -@Slf4j -public class JacksonVdmObjectDeserializer extends StdDeserializer> implements ContextualDeserializer -{ - private static final long serialVersionUID = -8440640969356203625L; - private JavaType valueType; - - /** - * Default constructor. - */ - protected JacksonVdmObjectDeserializer() - { - super((Class>) null); - } - - @Override - @Nonnull - public - JacksonVdmObjectDeserializer - createContextual( @Nonnull final DeserializationContext ctxt, @Nullable final BeanProperty property ) - { - final Option beanType = Option.of(property).map(BeanProperty::getType).map(t -> t.containedType(0)); - final JavaType valueType = beanType.getOrElse(ctxt::getContextualType); - final JacksonVdmObjectDeserializer deserializer = new JacksonVdmObjectDeserializer(); - deserializer.valueType = valueType; - return deserializer; - } - - @Override - @Nullable - public VdmObject deserialize( @Nonnull final JsonParser parser, @Nonnull final DeserializationContext ctxt ) - throws IOException - { - final Class vdmObjectType = valueType.getRawClass(); - final VdmObject vdmObject = instantiateVdmObject(vdmObjectType); - - // Handle current (first) JSON token - final JsonToken firstToken = parser.currentToken(); - if( firstToken == JsonToken.VALUE_NULL ) { - log.trace("VDM object is null"); - return null; - } - if( firstToken != JsonToken.START_OBJECT ) { - log.debug("VDM object can only be deserialized from JSON object."); - throw new IOException( - "Expected Start of JSON Object when deserializing an VDM object. Instead there was " + firstToken); - } - - // Create mapping for OData element name to actual Java field(s) - final Map> fieldValues = - Arrays - .stream(vdmObjectType.getDeclaredFields()) - .filter(f -> f.getAnnotation(ElementName.class) != null) - .collect(Collectors.groupingBy(f -> f.getDeclaredAnnotation(ElementName.class).value())); - - // Iterate JSON token handling - for( JsonToken token = parser.nextToken(); token != JsonToken.END_OBJECT; token = parser.nextToken() ) { - if( token != JsonToken.FIELD_NAME ) { - throw new IOException( - "Expected field name at current position of JSON object. Instead there was " + token); - } - final String fieldName = parser.currentName(); - - // Step from JSON element name to element value - parser.nextToken(); - - // Handle custom value when its field name is not declared on VDM object definition - if( !fieldValues.containsKey(fieldName) ) { - final Object value = ctxt.readValue(parser, Object.class); - handleCustomValue(vdmObject, fieldName, value); - continue; - } - - // Handle value for declared VDM object field - Object fieldValue = null; - for( final Field field : fieldValues.get(fieldName) ) { - if( fieldValue == null ) { - fieldValue = getVdmObjectFieldValue(field, parser, ctxt); - } - setVdmObjectFieldValue(vdmObject, field, fieldValue); - } - } - return vdmObject; - } - - @Nonnull - private VdmObject instantiateVdmObject( @Nonnull final Class vdmObjectType ) - throws IOException - { - try { - final Constructor declaredConstructor = vdmObjectType.getDeclaredConstructor(); - return (VdmObject) declaredConstructor.newInstance(); - } - catch( final - NoSuchMethodException - | InstantiationException - | IllegalArgumentException - | SecurityException - | IllegalAccessException - | InvocationTargetException e ) { - throw new IOException("Failed to create an instance from VDM object of type " + vdmObjectType, e); - } - } - - @Nullable - private Object getVdmObjectFieldValue( - @Nonnull final Field field, - @Nonnull final JsonParser parser, - @Nonnull final DeserializationContext ctxt ) - throws IOException - { - final JsonToken valueToken = parser.currentToken(); - - final JsonDeserialize customDeserialize = field.getAnnotation(JsonDeserialize.class); - if( customDeserialize != null ) { - try { - final AnnotationMap annotations = AnnotationMap.of(JsonDeserialize.class, customDeserialize); - final AnnotatedField annotated = new AnnotatedField(null, field, annotations); - final Object rawDeserializerType = ctxt.getAnnotationIntrospector().findDeserializer(annotated); - final Object rawDeserializer = ((Class) rawDeserializerType).getDeclaredConstructor().newInstance(); - @SuppressWarnings( "unchecked" ) - final JsonDeserializer deserializer = (JsonDeserializer) rawDeserializer; - return deserializer.deserialize(parser, ctxt); - } - catch( final Exception e ) { - final String msg = - String.format("Failed to use custom deserializer %s for field %s.", customDeserialize, field); - log.debug(msg, e); - throw new IOException(msg, e); - } - } - - if( valueToken == JsonToken.VALUE_STRING ) { - final String fieldValueString = parser.getText(); - final Option convertedObject = withCustomConverter(field.getType(), fieldValueString); - if( convertedObject.isDefined() ) { - return convertedObject.get(); - } - } - if( valueToken == JsonToken.VALUE_NULL ) { - return null; - } else { - final JavaType javaType = ctxt.constructType(field.getGenericType()); - return ctxt.readValue(parser, javaType); - } - } - - private void setVdmObjectFieldValue( - @Nonnull final VdmObject vdmObject, - @Nonnull final Field field, - @Nullable final Object value ) - throws IOException - { - try { - field.setAccessible(true); - field.set(vdmObject, value); - } - catch( final IllegalArgumentException | SecurityException | IllegalAccessException e ) { - final String className = vdmObject.getClass().getSimpleName(); - final String msg = String.format("Failed to set value for field %s on instance of %s", field, className); - log.debug(msg, e); - throw new IOException(msg, e); - } - } - - private void handleCustomValue( - @Nonnull final VdmObject object, - @Nonnull final String name, - @Nullable final Object value ) - { - if( Arrays.asList(VdmObject.ODATA_TYPE_ANNOTATIONS).contains(name) ) { - // do nothing - return; - } - if( object instanceof VdmEntity && Arrays.asList(VdmObject.ODATA_VERSION_ANNOTATIONS).contains(name) ) { - ((VdmEntity) object).setVersionIdentifier(String.valueOf(value)); - return; - } - object.setCustomField(name, value); - } - - @Nonnull - private Option withCustomConverter( @Nonnull final Class targetType, @Nonnull final String value ) - throws IOException - { - final Optional> matchingConverter = - Arrays - .stream(ODataGenericConverter.DEFAULT_CONVERTERS) - .filter(c -> targetType.isAssignableFrom(c.getType())) - .findFirst(); - - if( !matchingConverter.isPresent() ) { - return Option.none(); - } - - @SuppressWarnings( "unchecked" ) - final ConvertedObject convertedObject = - matchingConverter - .map(oDataGenericConverter -> ((ODataGenericConverter) oDataGenericConverter).fromDomain(value)) - .orElseGet(ConvertedObject::ofNotConvertible); - if( convertedObject.isNotConvertible() ) { - throw new IOException(String.format("Failed to convert %s to %s", value, targetType.getSimpleName())); - } - return Option.of(convertedObject.get()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmObjectSerializer.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmObjectSerializer.java deleted file mode 100644 index 9fdc394243..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/JacksonVdmObjectSerializer.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; -import java.util.stream.Collectors; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.BeanProperty; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.introspect.AnnotatedField; -import com.fasterxml.jackson.databind.introspect.AnnotationMap; -import com.fasterxml.jackson.databind.ser.ContextualSerializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmObject; -import com.sap.cloud.sdk.result.ElementName; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -import io.vavr.control.Option; -import io.vavr.control.Try; -import lombok.extern.slf4j.Slf4j; - -/** - * Jackson serializer adapter for {@link VdmEntity} and {@link VdmObject} types. - */ -@Slf4j -public class JacksonVdmObjectSerializer extends StdSerializer> implements ContextualSerializer -{ - private static final long serialVersionUID = 3559044362941940279L; - - /** - * Default constructor. - */ - protected JacksonVdmObjectSerializer() - { - super((Class>) null); - } - - @Override - public void serialize( - @Nullable final VdmObject object, - @Nonnull final JsonGenerator gen, - @Nonnull final SerializerProvider prov ) - throws IOException - { - if( object == null ) { - gen.writeNull(); - return; - } - final boolean nonNull = - prov.getDefaultPropertyInclusion(object.getType()).getContentInclusion() == JsonInclude.Include.NON_NULL; - - gen.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN); - gen.writeStartObject(); - - // odata type - final String odataType = "#" + object.getOdataType(); - gen.writeStringField(VdmObject.ODATA_TYPE_ANNOTATIONS[0], odataType); - - // odata version - if( object instanceof VdmEntity ) { - final Option versionIdentifier = ((VdmEntity) object).getVersionIdentifier(); - if( versionIdentifier.isDefined() ) { - gen.writeStringField(VdmObject.ODATA_TYPE_ANNOTATIONS[0], versionIdentifier.get()); - } - } - - final Map> fieldValues = - Arrays - .stream(object.getClass().getDeclaredFields()) - .filter(f -> f.getAnnotation(ElementName.class) != null) - .collect(Collectors.toMap(Function.identity(), f -> getFieldValue(f, object))); - - for( final Map.Entry> propertyEntry : fieldValues.entrySet() ) { - final Option propertyValueOption = propertyEntry.getValue(); - final Field propertyField = propertyEntry.getKey(); - if( propertyValueOption.isEmpty() ) { - log.trace("Field value for {} is empty.", propertyField); - if( !nonNull ) { - gen.writeNull(); - } - } else { - final Object propertyValue = propertyValueOption.get(); - writePropertyValue(propertyField, propertyValue, nonNull, gen, prov); - } - } - gen.writeEndObject(); - } - - private void writePropertyValue( - @Nonnull final Field propertyField, - @Nullable final Object propertyValue, - final boolean isNonNull, - @Nonnull final JsonGenerator gen, - @Nonnull final SerializerProvider prov ) - throws IOException - { - final String propertyName = propertyField.getAnnotation(ElementName.class).value(); - final JsonSerialize customSerialize = propertyField.getAnnotation(JsonSerialize.class); - if( customSerialize != null ) { - try { - gen.writeFieldName(propertyName); - final AnnotationMap annotations = AnnotationMap.of(JsonSerialize.class, customSerialize); - final AnnotatedField annotated = new AnnotatedField(null, propertyField, annotations); - final Object rawSerializerType = prov.getAnnotationIntrospector().findSerializer(annotated); - final Object rawSerializer = ((Class) rawSerializerType).getDeclaredConstructor().newInstance(); - @SuppressWarnings( "unchecked" ) - final JsonSerializer serializer = (JsonSerializer) rawSerializer; - serializer.serialize(propertyValue, gen, prov); - return; - } - catch( final Exception e ) { - final String msg = - String - .format( - "Failed to use custom serializer %s for field %s and value %s", - customSerialize, - propertyField, - propertyValue); - log.debug(msg, e); - throw new IOException(msg, e); - } - } - - final ConvertedObject convertedValue = withCustomConverter(propertyValue); - if( convertedValue.isConvertible() ) { - final String value = convertedValue.get(); - if( value != null || !isNonNull ) { - gen.writeStringField(propertyName, value); - } - } else { - if( propertyValue != null || !isNonNull ) { - prov.defaultSerializeField(propertyName, propertyValue, gen); - } - } - } - - @SuppressWarnings( "unchecked" ) - @Nonnull - private ConvertedObject withCustomConverter( @Nullable final Object v ) - { - final Optional> matchingConverter = - Arrays.stream(ODataGenericConverter.DEFAULT_CONVERTERS).filter(c -> c.getType().isInstance(v)).findFirst(); - return matchingConverter - .map(oDataGenericConverter -> ((ODataGenericConverter) oDataGenericConverter).toDomain(v)) - .orElseGet(ConvertedObject::ofNotConvertible); - } - - @Nonnull - private Option getFieldValue( @Nonnull final Field f, @Nonnull final VdmObject object ) - { - return Try.of(() -> { - f.setAccessible(true); - return f.get(object); - }).onFailure(e -> log.error("Failed to get value for field {} in object {}", f, object, e)).toOption(); - } - - @Override - @Nonnull - public - JsonSerializer - createContextual( @Nonnull final SerializerProvider prov, @Nullable final BeanProperty property ) - { - return new JacksonVdmObjectSerializer(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/ODataGenericConverter.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/ODataGenericConverter.java deleted file mode 100644 index fb0639c1ab..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/ODataGenericConverter.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import java.time.Duration; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Base64; -import java.util.UUID; -import java.util.function.Function; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.typeconverter.AbstractTypeConverter; -import com.sap.cloud.sdk.typeconverter.ConvertedObject; - -import io.vavr.control.Try; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * Generic fluent helper converters for String based OData V4 primitives. - * - * @param - * The Java type to which conversion happens. - */ -@RequiredArgsConstructor( access = AccessLevel.PRIVATE ) -final class ODataGenericConverter extends AbstractTypeConverter -{ - private static final ODataGenericConverter LOCAL_DATE = - new ODataGenericConverter<>( - LocalDate.class, - o -> o.format(DateTimeFormatter.ISO_LOCAL_DATE), - s -> LocalDate.parse(s, DateTimeFormatter.ISO_LOCAL_DATE)); - - private static final ODataGenericConverter LOCAL_TIME = - new ODataGenericConverter<>( - LocalTime.class, - o -> o.format(DateTimeFormatter.ISO_LOCAL_TIME), - s -> LocalTime.parse(s, DateTimeFormatter.ISO_LOCAL_TIME)); - - private static final ODataGenericConverter OFFSET_DATE_TIME = - new ODataGenericConverter<>( - OffsetDateTime.class, - o -> o.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), - s -> OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)); - - private static final ODataGenericConverter GUID = - new ODataGenericConverter<>(UUID.class, UUID::toString, UUID::fromString); - - private static final ODataGenericConverter DURATION = - new ODataGenericConverter<>(Duration.class, Duration::toString, Duration::parse); - - private static final ODataGenericConverter BINARY = - new ODataGenericConverter<>( - byte[].class, - Base64.getEncoder()::encodeToString, - ODataGenericConverter::decodeBinary); - - private static final ODataGenericConverter STRING = - new ODataGenericConverter<>(String.class, Function.identity(), Function.identity()); - - /** - * Array of OData value converters for primitive types. - */ - public static final ODataGenericConverter[] DEFAULT_CONVERTERS = - { LOCAL_DATE, LOCAL_TIME, OFFSET_DATE_TIME, GUID, DURATION, BINARY, STRING }; - - @Getter - private final Class type; - - @Getter - private final Class domainType = String.class; - - private final Function serializer; - private final Function deserializer; - - @Nonnull - private static byte[] decodeBinary( @Nonnull final String value ) - { - // Normalize URL-safe characters to standard Base64 - final String normalized = value.replace('-', '+').replace('_', '/'); - - return Base64.getDecoder().decode(normalized); - } - - @Nonnull - @Override - public ConvertedObject toDomainNonNull( @Nonnull final JavaT object ) - { - return ConvertedObject.of(serializer.apply(object)); - } - - @Nonnull - @Override - public ConvertedObject fromDomainNonNull( @Nonnull final String domainObject ) - { - final Try maybe = Try.of(() -> deserializer.apply(domainObject)); - return maybe.map(ConvertedObject::of).getOrElse(ConvertedObject::ofNotConvertible); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractBoundOperation.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractBoundOperation.java deleted file mode 100644 index c0701dda07..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractBoundOperation.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataFunctionParameters; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@Getter -@RequiredArgsConstructor -abstract class AbstractBoundOperation implements BoundOperation -{ - @Nonnull - private final Class bindingType; - @Nonnull - private final Class returnType; - @Nonnull - private final String qualifiedName; - - abstract static class AbstractBoundFunction extends AbstractBoundOperation - implements - BoundFunction - { - @Getter( AccessLevel.PUBLIC ) - @Nonnull - private final ODataFunctionParameters parameters; - - AbstractBoundFunction( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name); - parameters = ODataFunctionParameters.of(args, ODataProtocol.V4); - } - } - - abstract static class AbstractBoundAction extends AbstractBoundOperation - implements - BoundAction - { - @Getter - @Nonnull - private final Map parameters; - - AbstractBoundAction( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map parameters ) - { - super(src, target, name); - this.parameters = parameters; - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractEntityBasedRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractEntityBasedRequestBuilder.java deleted file mode 100644 index 24cd952279..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractEntityBasedRequestBuilder.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; - -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of OData requests that operate on entities as a fluent interface for further configuring the request - * and {@link #execute(com.sap.cloud.sdk.cloudplatform.connectivity.Destination) executing} it. - * - * @param - * The specific request builder type. - * @param - * The type of the entity this OData request operates on, if any. - * @param - * The type of the result entity, if any. - */ -@Slf4j -abstract class AbstractEntityBasedRequestBuilder, EntityT extends VdmEntity, ResultT> - extends - AbstractRequestBuilder -{ - /** - * Returns a class object of the type this request builder works with. - * - * @return A class object of the handled type. - */ - @Nonnull - protected abstract Class getEntityClass(); - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityPath - * The resource path pointing to the entity (collection) this request should operate on. - */ - AbstractEntityBasedRequestBuilder( @Nonnull final String servicePath, @Nonnull final ODataResourcePath entityPath ) - { - super(servicePath, entityPath); - } - - @Nonnull - static > String getEntityCollectionFromEntityClass( - @Nonnull final Class entityClass ) - { - return new VdmEntityUtil<>(entityClass).newInstance().getEntityCollection(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractRequestBuilder.java deleted file mode 100644 index ae5926b15a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractRequestBuilder.java +++ /dev/null @@ -1,200 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestListener; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataUriFactory; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of a generic OData request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. - * - * @param - * The specific builder type. - * @param - * The type of the result entity, if any. - */ -@Slf4j -abstract class AbstractRequestBuilder, ResultT> - implements - RequestBuilder -{ - @Nonnull - @Getter( AccessLevel.PROTECTED ) - private final String servicePath; - - @Nonnull - @Getter( AccessLevel.PROTECTED ) - private final ODataResourcePath resourcePath; - - /** - * A map containing the headers to be used only for the actual request of this FluentHelper implementation. - */ - @Getter( AccessLevel.PROTECTED ) - @Nonnull - private final Map> headers = new HashMap<>(); - - /** - * A map containing the custom query parameters to be used only for the actual request of this FluentHelper - * implementation. - */ - @Getter( AccessLevel.PROTECTED ) - @Nonnull - private final Map parametersForRequestOnly = new HashMap<>(); - - @Getter( AccessLevel.PROTECTED ) - @Nonnull - private final List listeners = new ArrayList<>(); - - /** - * Whether the CSRF token retrieval should be skipped for the actual request of this request builder. - */ - private boolean skipCsrfTokenRetrieval = false; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param resourcePath - * The resource path identifying the resource to operate on. - */ - public AbstractRequestBuilder( @Nonnull final String servicePath, @Nonnull final ODataResourcePath resourcePath ) - { - this.servicePath = servicePath; - this.resourcePath = resourcePath; - } - - /** - * Get the reference to this instance. - * - * @return The FluentHelper instance. - */ - @SuppressWarnings( "unchecked" ) - @Nonnull - protected BuilderT getThis() - { - return (BuilderT) this; - } - - /** - * An error handling class that implements the error result handler interface can be attached to this request - * builder. This allows custom logic to be called when an error occurs in the {@link #execute execute} method. If - * this method is not called, then an instance of ODataRequestListener is used. Only one handler can be attached at - * a time per request builder object, so calling this multiple times will replace the handler. - * - * @param listener - * Instance of an error handler class that implements the error result handler interface. - * - * @return The same request builder with its error handler set to the provided object. - */ - @Nonnull - public BuilderT withListener( @Nonnull final ODataRequestListener listener ) - { - this.listeners.add(listener); - return getThis(); - } - - @Nonnull - @Override - public BuilderT withHeader( @Nonnull final String key, @Nullable final String value ) - { - headers.computeIfAbsent(key, k -> new ArrayList<>(1)).add(value); - return getThis(); - } - - @Nonnull - @Override - public BuilderT withHeaders( @Nonnull final Map map ) - { - map.forEach(this::withHeader); - return getThis(); - } - - /** - * Gives the option to specify custom query parameters for the request. - * - *

- * Note: It is recommended to only use this function for query parameters which are not supported - * by the VDM by default. Using this function to bypass request builder method calls can lead to unsupported - * response handling. There is no contract on the order or priority of parameters added to the request. - *

- * - *

- * Example: Use the request query option $search to reduce the result set, leaving - * only entities which match the specified search expression. This feature is supported in protocol OData v4. - * - *

-     * new DefaultBusinessPartnerService().getAllBusinessPartner().withQueryParameter("$search", "Köln OR Cologne")
-     * 
- *

- * - * @param key - * Name of the query parameter. - * @param value - * Value of the query parameter. - * - * @return The same request builder. - */ - @Nonnull - public BuilderT withQueryParameter( @Nonnull final String key, @Nullable final String value ) - { - parametersForRequestOnly.put(key, value); - return getThis(); - } - - @Nonnull - RequestT toRequest( @Nonnull final RequestT request ) - { - getHeaders().forEach(( k, values ) -> values.forEach(v -> request.addHeader(k, v))); - - getParametersForRequestOnly() - .forEach(( key, value ) -> request.addQueryParameter(key, ODataUriFactory.encodeQuery(value))); - - getListeners().forEach(request::addListener); - - return request; - } - - /** - * Marks this request builder to skip CSRF token retrieval for its actual request, so the request is sent with an - * {@link HttpClient} that does not have the CSRF token interceptor enabled. - */ - protected void setSkipCsrfTokenRetrieval() - { - skipCsrfTokenRetrieval = true; - } - - /** - * Returns the {@link HttpClient} to be used for the actual request of this request builder. If CSRF token retrieval - * was disabled via {@code withoutCsrfToken()}, a client without the CSRF token interceptor is returned. - * - * @param destination - * The destination to get the {@link HttpClient} for. - * @return An {@link HttpClient} for the given destination. - */ - @Nonnull - protected HttpClient getHttpClient( @Nonnull final Destination destination ) - { - return skipCsrfTokenRetrieval - ? ODataApacheHttpClient5Accessor.getHttpClientWithoutCsrf(destination) - : ODataApacheHttpClient5Accessor.getHttpClient(destination); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractStructuredPropertyQuery.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractStructuredPropertyQuery.java deleted file mode 100644 index 33b42073a8..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/AbstractStructuredPropertyQuery.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.cloudplatform.exception.ShouldNotHappenException; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldUntyped; -import com.sap.cloud.sdk.datamodel.odata.client.query.StructuredQuery; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * Implementation that represents read queries and holds their state at runtime. It allows for nested queries in a - * recursive manner. The implementation is the same for queries over single entities and collections of entities. In the - * VDM the available functionality is limited by the interfaces. - * - * In order to support a fluent creation of nested queries both the entity and the parent entity type are stored via - * generics. By implementing {@link StructuredProperty} the API doesn't differentiate between selections via referencing - * navigational properties and selections via sub-queries. - * - * @param - * The generic navigation property entity source type. - * @param - * The generic navigation property entity target type. - */ -@RequiredArgsConstructor( access = AccessLevel.PACKAGE ) -abstract class AbstractStructuredPropertyQuery, EntityT extends VdmObject> - implements - ProtocolQueryRead, - StructuredProperty -{ - /** - * The delegate query. - */ - @Getter( AccessLevel.PROTECTED ) - protected final StructuredQuery delegateQuery; - - /** - * Query modifier to limit which field values of the entity {@linkplain EntityT} get fetched and populated. - * - * @param fields - * Properties of {@linkplain EntityT} to be selected. - * @return This query object with the added selections. - */ - @Nonnull - protected final AbstractStructuredPropertyQuery select( - @Nonnull final Iterable> fields ) - { - for( final Property field : fields ) { - if( field instanceof SimpleProperty || field instanceof ComplexProperty ) { - delegateQuery.select(field.getFieldName()); - continue; - - } - if( field instanceof StructuredProperty ) { - if( field instanceof ComplexPropertyQuery ) { - for( final String simpleSelector : ((ComplexPropertyQuery) field) - .getDelegateQuery() - .getSimpleSelectors() ) { - final FieldUntyped untypedField = FieldReference.ofPath(field.getFieldName(), simpleSelector); - delegateQuery.select(untypedField.getFieldName()); - } - - for( final StructuredQuery subQuery : ((ComplexPropertyQuery) field) - .getDelegateQuery() - .getComplexSelectors() ) { - - final FieldUntyped untypedField = - FieldReference.ofPath(field.getFieldName(), subQuery.getEntityOrPropertyName()); - delegateQuery - .select( - StructuredQuery - .asNestedQueryOnProperty(untypedField.getFieldName(), ODataProtocol.V4) - .select(subQuery.getSimpleSelectors().toArray(new String[0])) - .select(subQuery.getComplexSelectors().toArray(new StructuredQuery[0]))); - } - } else if( field instanceof AbstractStructuredPropertyQuery ) { - delegateQuery.select(((AbstractStructuredPropertyQuery) field).getDelegateQuery()); - } else { - delegateQuery - .select(StructuredQuery.asNestedQueryOnProperty(field.getFieldName(), ODataProtocol.V4)); - } - - continue; - } - throw new ShouldNotHappenException(); - } - return this; - } - - @Nonnull - @Override - public String getFieldName() - { - return delegateQuery.getEntityOrPropertyName(); - } - - @Nonnull - String getEncodedQueryString() - { - return delegateQuery.getEncodedQueryString(); - } - - @Nonnull - String getQueryString() - { - return delegateQuery.getQueryString(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ActionRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ActionRequestBuilder.java deleted file mode 100644 index 18ceb90c59..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ActionRequestBuilder.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Collections; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; -import com.google.gson.TypeAdapter; -import com.google.gson.reflect.TypeToken; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestAction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestFunction; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory; - -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an Action OData request as a fluent interface for further configuring the request and - * {@link #execute(com.sap.cloud.sdk.cloudplatform.connectivity.Destination) executing} it. - * - * @param - * The request builder type. - * @param - * The type of the result, if any. - */ -@Slf4j -public abstract class ActionRequestBuilder, ResultT> - extends - AbstractRequestBuilder - implements - ModificationRequestBuilder -{ - private static final Gson gson = new GsonBuilder().serializeNulls().create(); - - private static final GsonVdmAdapterFactory GSON_VDM_ADAPTER_FACTORY = new GsonVdmAdapterFactory(); - private final Map parameters; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param actionName - * The name of the unbound action - */ - public ActionRequestBuilder( @Nonnull final String servicePath, @Nonnull final String actionName ) - { - this(servicePath, actionName, Collections.emptyMap()); - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param actionName - * The name of the unbound action. - * @param parameters - * The parameters passed to the function. - */ - public ActionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final String actionName, - @Nonnull final Map parameters ) - { - this(servicePath, ODataResourcePath.of(actionName), parameters); - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param actionPath - * The path to the unbound action. - * @param parameters - * The parameters passed to the function. - */ - public ActionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath actionPath, - @Nonnull final Map parameters ) - { - super(servicePath, actionPath); - this.parameters = parameters; - } - - /** - * Serializes the passed parameter with custom type adapters and returns a JsonElement. - */ - @Nonnull - private JsonElement serialize( @Nullable final T parameter ) - { - if( parameter == null ) { - return JsonNull.INSTANCE; - } - - @SuppressWarnings( "unchecked" ) - final TypeToken typeToken = TypeToken.get((Class) parameter.getClass()); - final TypeAdapter typeAdapter = GSON_VDM_ADAPTER_FACTORY.create(gson, typeToken); - - final JsonElement jsonObject; - if( typeAdapter != null ) { - jsonObject = typeAdapter.toJsonTree(parameter); - } else { - jsonObject = gson.toJsonTree(parameter); - } - return jsonObject; - } - - /** - * Creates an instance of {@link ODataRequestAction} based on the Entity class. - *

- * The following settings are used: - *

    - *
  • the endpoint URL
  • - *
  • the action name
  • - *
  • the parameters if applicable as JSON string
  • - *
- * - * @return An initialized {@link ODataRequestFunction}. - */ - @Override - @Nonnull - public ODataRequestAction toRequest() - { - final ODataRequestAction request = - new ODataRequestAction(getServicePath(), getResourcePath(), serializeParameters(), ODataProtocol.V4); - return super.toRequest(request); - } - - @Nonnull - private String serializeParameters() - { - final JsonObject o = new JsonObject(); - parameters.forEach(( key, value ) -> o.add(key, serialize(value))); - - return gson.toJson(o); - } - - /** - * Deactivates the CSRF token retrieval for this OData request. This is useful if the server does not support or - * require CSRF tokens as part of the request. - * - * @return The same builder - */ - @Override - @Nonnull - public BuilderT withoutCsrfToken() - { - setSkipCsrfTokenRetrieval(); - return getThis(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ActionResponseCollection.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ActionResponseCollection.java deleted file mode 100644 index 3d8005447c..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ActionResponseCollection.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import io.vavr.control.Option; -import io.vavr.control.Try; -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -/** - * Generic OData service response wrapper for action requests. - * - * @param - * The generic result type. - */ -@EqualsAndHashCode -@ToString( doNotUseGetters = true ) -@RequiredArgsConstructor( staticName = "of", access = AccessLevel.PUBLIC ) -@Slf4j -public final class ActionResponseCollection -{ - // lazily evaluated response entity - private volatile Option> responseResult = null; - private final Object responseResultLock = new Object(); - - @Nonnull - private final ODataRequestResultGeneric result; - - @Getter - @Nonnull - private final Class actionResultClass; - - /** - * Get the optional result parsed by the HTTP content. - * - * @return The optional result entity. - */ - @Nonnull - public Option> getResponseResult() - { - if( responseResult == null ) { - synchronized( responseResultLock ) { - if( responseResult == null ) { - responseResult = parseEntityFromResponse(); - } - } - } - return responseResult; - } - - /** - * Get the response status code. - * - * @return The integer representation of the HTTP status code. - */ - public int getResponseStatusCode() - { - return result.getHttpResponse().getCode(); - } - - /** - * Get the response headers. - * - * @return The headers of the HTTP status code. - */ - @Nonnull - public Map> getResponseHeaders() - { - return result.getAllHeaderValues(); - } - - @Nonnull - private Option> parseEntityFromResponse() - { - if( actionResultClass.equals(Void.class) ) { - return Option.none(); - } - @SuppressWarnings( "unchecked" ) - final Try> parsedEntity = Try.of(() -> result.asList(actionResultClass)); - return parsedEntity.onFailure(e -> log.debug("Failed to parse entity from HTTP response.", e)).toOption(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ActionResponseSingle.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ActionResponseSingle.java deleted file mode 100644 index 5d3e107f58..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ActionResponseSingle.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import io.vavr.control.Option; -import io.vavr.control.Try; -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -/** - * Generic OData service response wrapper for action requests. - * - * @param - * The generic result type. - */ -@EqualsAndHashCode -@ToString( doNotUseGetters = true ) -@RequiredArgsConstructor( staticName = "of", access = AccessLevel.PUBLIC ) -@Slf4j -public final class ActionResponseSingle -{ - // lazily evaluated response entity - private volatile Option responseResult = null; - private final Object responseResultLock = new Object(); - - @Nonnull - private final ODataRequestResultGeneric result; - - @Getter - @Nonnull - private final Class actionResultClass; - - /** - * Get the optional result parsed by the HTTP content. - * - * @return The optional result entity. - */ - @Nonnull - public Option getResponseResult() - { - if( responseResult == null ) { - synchronized( responseResultLock ) { - if( responseResult == null ) { - responseResult = parseEntityFromResponse(); - } - } - } - return responseResult; - } - - /** - * Get the response status code. - * - * @return The integer representation of the HTTP status code. - */ - public int getResponseStatusCode() - { - return result.getHttpResponse().getCode(); - } - - /** - * Get the response headers. - * - * @return The headers of the HTTP status code. - */ - @Nonnull - public Map> getResponseHeaders() - { - return result.getAllHeaderValues(); - } - - @Nonnull - private Option parseEntityFromResponse() - { - if( actionResultClass.equals(Void.class) ) { - return Option.none(); - } - final Try parsedEntity = Try.of(() -> result.as(actionResultClass)).peek(entity -> { - if( entity instanceof VdmEntity ) { - result.getVersionIdentifierFromHeader().peek(((VdmEntity) entity)::setVersionIdentifier); - } - }); - return parsedEntity.onFailure(e -> log.debug("Failed to parse entity from HTTP response.", e)).toOption(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BatchRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BatchRequestBuilder.java deleted file mode 100644 index 8e7bf8c398..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BatchRequestBuilder.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.UUID; -import java.util.function.Supplier; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestAction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCreate; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestDelete; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestFunction; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestReadByKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultMultipartGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestUpdate; - -import lombok.AccessLevel; -import lombok.Getter; - -/** - * Representation of an OData Batch request as a fluent interface for combining multiple data reading and modifying - * operations in one HTTP request. - */ -public class BatchRequestBuilder extends AbstractRequestBuilder - implements - ModificationRequestBuilder -{ - private final ODataRequestBatch delegate; - - @Getter( AccessLevel.PROTECTED ) - private final Supplier uuidProvider = UUID::randomUUID; - - @Nonnull - @Getter( AccessLevel.PACKAGE ) - private final Map, ODataRequestGeneric> requestMapping = new IdentityHashMap<>(); - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - */ - @SuppressWarnings( "this-escape" ) // getUuidProvider() is designed to be overridable. - public BatchRequestBuilder( @Nonnull final String servicePath ) - { - super(servicePath, ODataResourcePath.of("$batch")); - this.delegate = new ODataRequestBatch(servicePath, ODataProtocol.V4, getUuidProvider()); - } - - /** - * Add read operations to the OData batch request. - * - * @param operations - * A var-arg array of read operations. - * @return The current reference of batch request builder. - * @see GetAllRequestBuilder - * @see GetByKeyRequestBuilder - * @see FunctionRequestBuilder - */ - @Nonnull - public BatchRequestBuilder addReadOperations( @Nonnull final ReadRequestBuilder... operations ) - { - for( final ReadRequestBuilder operation : operations ) { - final ODataRequestGeneric request = operation.toRequest(); - requestMapping.put(operation, request); - if( request instanceof ODataRequestRead ) { - delegate.addRead((ODataRequestRead) request); - } else if( request instanceof ODataRequestReadByKey ) { - delegate.addReadByKey((ODataRequestReadByKey) request); - } else if( request instanceof ODataRequestFunction ) { - delegate.addFunction((ODataRequestFunction) request); - } else { - throw new IllegalArgumentException( - "Failed to add unknown type of read operation to OData batch request: " - + operation.getClass().getSimpleName()); - } - } - return this; - } - - /** - * Add modifying operations to the OData batch request as combined changeset. - * - * @param operations - * A var-arg array of modifying operations. - * @return The current reference of batch request builder. - * @see CreateRequestBuilder - * @see DeleteRequestBuilder - * @see UpdateRequestBuilder - * @see ActionRequestBuilder - */ - @Nonnull - public BatchRequestBuilder addChangeset( @Nonnull final ModificationRequestBuilder... operations ) - { - final ODataRequestBatch.Changeset changeset = delegate.beginChangeset(); - for( final ModificationRequestBuilder operation : operations ) { - final ODataRequestGeneric request = operation.toRequest(); - requestMapping.put(operation, request); - if( request instanceof ODataRequestCreate ) { - changeset.addCreate((ODataRequestCreate) request); - } else if( request instanceof ODataRequestUpdate ) { - changeset.addUpdate((ODataRequestUpdate) request); - } else if( request instanceof ODataRequestDelete ) { - changeset.addDelete((ODataRequestDelete) request); - } else if( request instanceof ODataRequestAction ) { - changeset.addAction((ODataRequestAction) request); - } else { - throw new IllegalArgumentException( - "Failed to add unknown type of modifying operation to OData batch request: " - + operation.getClass().getSimpleName()); - } - } - changeset.endChangeset(); - return this; - } - - @Override - @Nonnull - public ODataRequestBatch toRequest() - { - return super.toRequest(delegate); - } - - @Override - @Nonnull - public BatchRequestBuilder withoutCsrfToken() - { - setSkipCsrfTokenRetrieval(); - return this; - } - - @Nonnull - @Override - public BatchResponse execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - - @SuppressWarnings( "PMD.CloseResource" ) // The ODataRequestResultMultipartGeneric is closed by BatchResponse - final ODataRequestResultMultipartGeneric response = toRequest().execute(httpClient); - - return BatchResponse.of(response, requestMapping); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BatchResponse.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BatchResponse.java deleted file mode 100644 index d4437ac570..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BatchResponse.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataResponseException; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultMultipartGeneric; - -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -/** - * Generic OData service response wrapper for Batch response. - * - */ -@EqualsAndHashCode -@ToString -@RequiredArgsConstructor( staticName = "of", access = AccessLevel.PACKAGE ) -@Slf4j -public final class BatchResponse implements AutoCloseable -{ - @Nonnull - private final ODataRequestResultMultipartGeneric result; - - @Nonnull - private final Map, ODataRequestGeneric> requestMapping; - - /** - * Static factory method to convert from generic response to typed response. - * - * @param response - * The generic response that should be converted. - * @param initialRequest - * The initial BatchRequest - * @return The typed (high-level) BatchResponse object. - */ - @Nonnull - public static BatchResponse of( - @Nonnull final ODataRequestResultMultipartGeneric response, - @Nonnull final BatchRequestBuilder initialRequest ) - { - return BatchResponse.of(response, initialRequest.getRequestMapping()); - } - - /** - * Get the response status code. - * - * @return The integer representation of the HTTP status code. - */ - public int getResponseStatusCode() - { - return result.getHttpResponse().getCode(); - } - - /** - * Get the response headers. - * - * @return The headers of the HTTP status code. - */ - @Nonnull - public Map> getResponseHeaders() - { - return result.getAllHeaderValues(); - } - - /** - * Extract the batch item result for the provided OData request. - * - * @param operation - * The OData operation that was used in the OData batch request. - * @param - * The generic entity type. - * @return The list of entities as result of the request. - * @throws ODataResponseException - * When the OData batch response cannot be parsed. - * @throws IllegalArgumentException - * When the provided request reference could not be found in the original batch request. - */ - @Nonnull - public > List getReadResult( @Nonnull final GetAllRequestBuilder operation ) - { - final ODataRequestResultGeneric clientResult = result.getResult(requestMapping.get(operation)); - return clientResult.asList(operation.getEntityClass()); - } - - /** - * Extract the batch item result for the provided OData request. - * - * @param operation - * The OData operation that was used in the OData batch request. - * @param - * The generic object type. - * @return The list of objects as result of the request. - * @throws ODataResponseException - * When the OData batch response cannot be parsed. - * @throws IllegalArgumentException - * When the provided request reference could not be found in the original batch request. - */ - @Nonnull - public List getReadResult( @Nonnull final CollectionValueFunctionRequestBuilder operation ) - { - final ODataRequestResultGeneric clientResult = result.getResult(requestMapping.get(operation)); - return clientResult.asList(operation.getResultClass()); - } - - /** - * Extract the batch item result for the provided OData request. - * - * @param operation - * The OData operation that was used in the OData batch request. - * @param - * The generic entity type. - * @return The list of entities as result of the request. - * @throws ODataResponseException - * When the OData batch response cannot be parsed. - * @throws IllegalArgumentException - * When the provided request reference could not be found in the original batch request. - */ - @Nonnull - public > T getReadResult( @Nonnull final GetByKeyRequestBuilder operation ) - { - final ODataRequestResultGeneric clientResult = result.getResult(requestMapping.get(operation)); - return clientResult.as(operation.getEntityClass()); - } - - /** - * Extract the batch item result for the provided OData request. - * - * @param operation - * The OData operation that was used in the OData batch request. - * @param - * The generic object type. - * @return The object as result of the request. - * @throws ODataResponseException - * When the OData batch response cannot be parsed. - * @throws IllegalArgumentException - * When the provided request reference could not be found in the original batch request. - */ - @Nonnull - public T getReadResult( @Nonnull final SingleValueFunctionRequestBuilder operation ) - { - final ODataRequestResultGeneric clientResult = result.getResult(requestMapping.get(operation)); - return clientResult.as(operation.getResultClass()); - } - - /** - * Extract the batch item result for the provided OData request. - * - * @param operation - * The OData operation that was used in the OData batch request. - * @param - * The generic entity type. - * @return The generic modification response wrapper object as result of the modification request. - * @throws ODataResponseException - * When the OData batch response cannot be parsed. - * @throws IllegalArgumentException - * When the provided request reference could not be found in the original batch request. - */ - @Nonnull - public > ModificationResponse getModificationResult( - @Nonnull final CreateRequestBuilder operation ) - { - final ODataRequestResultGeneric clientResult = result.getResult(requestMapping.get(operation)); - return ModificationResponse.of(clientResult, operation.getEntity()); - } - - /** - * Extract the batch item result for the provided OData request. - * - * @param operation - * The OData operation that was used in the OData batch request. - * @param - * The generic entity type. - * @return The generic modification response wrapper object as result of the modification request. - * @throws ODataResponseException - * When the OData batch response cannot be parsed. - * @throws IllegalArgumentException - * When the provided request reference could not be found in the original batch request. - */ - @Nonnull - public > ModificationResponse getModificationResult( - @Nonnull final UpdateRequestBuilder operation ) - { - final ODataRequestResultGeneric clientResult = result.getResult(requestMapping.get(operation)); - return ModificationResponse.of(clientResult, operation.getEntity()); - } - - /** - * Extract the batch item result for the provided OData request. - * - * @param operation - * The OData operation that was used in the OData batch request. - * @param - * The generic entity type. - * @return The generic modification response wrapper object as result of the modification request. - * @throws ODataResponseException - * When the OData batch response cannot be parsed. - * @throws IllegalArgumentException - * When the provided request reference could not be found in the original batch request. - */ - @Nonnull - public > ModificationResponse getModificationResult( - @Nonnull final DeleteRequestBuilder operation ) - { - final ODataRequestResultGeneric clientResult = result.getResult(requestMapping.get(operation)); - return ModificationResponse.of(clientResult, operation.getEntity()); - } - - /** - * Extract the batch item result for the provided OData request. - * - * @param operation - * The OData operation that was used in the OData batch request. - * @param - * The generic object type. - * @return The generic action response wrapper object as result of the OData action request. - * @throws ODataResponseException - * When the OData batch response cannot be parsed. - * @throws IllegalArgumentException - * When the provided request reference could not be found in the original batch request. - */ - @Nonnull - public < - T> ActionResponseSingle getModificationResult( @Nonnull final SingleValueActionRequestBuilder operation ) - { - final ODataRequestResultGeneric clientResult = result.getResult(requestMapping.get(operation)); - return ActionResponseSingle.of(clientResult, operation.getResultClass()); - } - - /** - * Extract the batch item result for the provided OData request. - * - * @param operation - * The OData operation that was used in the OData batch request. - * @param - * The generic object type. - * @return The generic action response wrapper object as result of the OData action request. - * @throws ODataResponseException - * When the OData batch response cannot be parsed. - * @throws IllegalArgumentException - * When the provided request reference could not be found in the original batch request. - */ - @Nonnull - public ActionResponseCollection getModificationResult( - @Nonnull final CollectionValueActionRequestBuilder operation ) - { - final ODataRequestResultGeneric clientResult = result.getResult(requestMapping.get(operation)); - return ActionResponseCollection.of(clientResult, operation.getResultClass()); - } - - /** - * Closes the underlying HTTP response entity. - * - * @since 4.15.0 - */ - @Override - public void close() - { - result.close(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundAction.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundAction.java deleted file mode 100644 index b44bfd00d4..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundAction.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; - -/** - * Interface representing an action bound to a specific type. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ -public interface BoundAction extends BoundOperation -{ - /** - * The parameters to invoke this action with. - * - * @return The parameters to invoke this action with. - */ - @Nonnull - Map getParameters(); - - /** - * Specific {@link BoundAction action} operating on a single element and returning an object, if any. - * - * @param - * The type the action is bound to. - * @param - * The type this action returns. - */ - final class SingleToSingle extends AbstractBoundOperation.AbstractBoundAction - { - /** - * Default constructor. - * - * @param src - * The type the action is bound to. - * @param target - * The type this action returns. - * @param name - * The fully qualified name of the action. - * @param parameters - * The parameters to invoke this action with. - */ - public SingleToSingle( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map parameters ) - { - super(src, target, name, parameters); - } - } - - /** - * Specific {@link BoundAction action} operating on a single element and returning a collection of objects. - * - * @param - * The type the action is bound to. - * @param - * The type this action returns. - */ - final class SingleToCollection - extends - AbstractBoundOperation.AbstractBoundAction - { - /** - * Default constructor. - * - * @param src - * The type the action is bound to. - * @param target - * The type this action returns. - * @param name - * The fully qualified name of the action. - * @param parameters - * The parameters to invoke this action with. - */ - public SingleToCollection( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map parameters ) - { - super(src, target, name, parameters); - } - } - - /** - * Specific {@link BoundAction action} operating on a collection of elements and returning an object, if any. - * - * @param - * The type the action is bound to. - * @param - * The type this action returns. - */ - final class CollectionToSingle - extends - AbstractBoundOperation.AbstractBoundAction - { - /** - * Default constructor. - * - * @param src - * The type the action is bound to. - * @param target - * The type this action returns. - * @param name - * The fully qualified name of the action. - * @param parameters - * The parameters to invoke this action with. - */ - public CollectionToSingle( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map parameters ) - { - super(src, target, name, parameters); - } - } - - /** - * Specific {@link BoundAction action} operating on a collection of element and returning a collection of objects. - * - * @param - * The type the action is bound to. - * @param - * The type this action returns. - */ - final class CollectionToCollection - extends - AbstractBoundOperation.AbstractBoundAction - { - /** - * Default constructor. - * - * @param src - * The type the action is bound to. - * @param target - * The type this action returns. - * @param name - * The fully qualified name of the action. - * @param parameters - * The parameters to invoke this action with. - */ - public CollectionToCollection( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map parameters ) - { - super(src, target, name, parameters); - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundFunction.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundFunction.java deleted file mode 100644 index 8fc44c2a70..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundFunction.java +++ /dev/null @@ -1,739 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataFunctionParameters; - -/** - * Interface representing a function bound to a specific type. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ -public interface BoundFunction extends BoundOperation -{ - /** - * The parameters this function is invoked with. - * - * @return The parameters this function is invoked with. - */ - @Nonnull - ODataFunctionParameters getParameters(); - - /* - we need to differentiate on the type system all following dimensions: - -> Composable (true/false) - -> Src Cardinality (single/collection) - -> Target Cardinality (single/collection) - -> Target Type (primitive, complex, entity) - - in total 24 combinations == 24 classes with this approach - */ - - /** - * Interface representing a composable bound function - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - interface Composable extends BoundFunction - { - } - - /*----------------------------------------------------------*/ - /* 1 - 1 Functions */ - /*----------------------------------------------------------*/ - - /** - * Specific {@link BoundFunction function} operating on a single element and returning a primitive. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class SingleToSinglePrimitive extends SingleToSingle - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function argument names and their values - */ - public SingleToSinglePrimitive( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a single element and returning a complex type. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class SingleToSingleComplex> extends SingleToSingle - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public SingleToSingleComplex( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a single element and returning an entity. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class SingleToSingleEntity> extends SingleToSingle - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public SingleToSingleEntity( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - - /** - * Class representing a composable bound function - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - public static final class Composable> - extends - SingleToSingleEntity - implements - BoundFunction.Composable - { - - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public Composable( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - } - - /*----------------------------------------------------------*/ - /* 1 - N Functions */ - /*----------------------------------------------------------*/ - - /** - * Specific {@link BoundFunction function} operating on a single element and returning a collection of primitives. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class SingleToCollectionPrimitive extends SingleToCollection - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public SingleToCollectionPrimitive( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a single element and returning a collection of complex - * objects. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class SingleToCollectionComplex> - extends - SingleToCollection - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public SingleToCollectionComplex( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a single element and returning a collection of entities. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class SingleToCollectionEntity> - extends - SingleToCollection - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public SingleToCollectionEntity( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - - /** - * Class representing a composable bound function - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - public static final class Composable> - extends - SingleToCollectionEntity - { - - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public Composable( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - } - - /*----------------------------------------------------------*/ - /* N - 1 Functions */ - /*----------------------------------------------------------*/ - /** - * Specific {@link BoundFunction function} operating on a collection of elements and returning a primitive. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class CollectionToSinglePrimitive extends CollectionToSingle - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public CollectionToSinglePrimitive( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a collection of elements and returning a complex object. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class CollectionToSingleComplex> - extends - CollectionToSingle - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public CollectionToSingleComplex( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a collection of elements and returning an entity. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class CollectionToSingleEntity> - extends - CollectionToSingle - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public CollectionToSingleEntity( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - - /** - * Class representing a composable bound function - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - public static final class Composable> - extends - CollectionToSingleEntity - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public Composable( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - } - - /*----------------------------------------------------------*/ - /* N - N Functions */ - /*----------------------------------------------------------*/ - /** - * Specific {@link BoundFunction function} operating on a collection of elements and returning a collection of - * primitives. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class CollectionToCollectionPrimitive extends CollectionToCollection - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public CollectionToCollectionPrimitive( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a collection of elements and returning a collection of - * complex objects. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class CollectionToCollectionComplex> - extends - CollectionToCollection - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public CollectionToCollectionComplex( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a collection of elements and returning a collection of - * entities. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class CollectionToCollectionEntity> - extends - CollectionToCollection - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public CollectionToCollectionEntity( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - - /** - * Class representing a composable bound function - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - public static final class Composable> - extends - CollectionToCollectionEntity - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public Composable( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - } - - /*----------------------------------------------------------*/ - /* Variants without target type info */ - /* used by "applyFunction" which doesn't care about the */ - /* target type to be in the type system */ - /*----------------------------------------------------------*/ - - /** - * Specific {@link BoundFunction function} operating on a single element returning a single element. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class SingleToSingle extends AbstractBoundOperation.AbstractBoundFunction - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public SingleToSingle( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a single element returning a collection of elements. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class SingleToCollection extends AbstractBoundOperation.AbstractBoundFunction - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public SingleToCollection( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a collection of elements returning a single element. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class CollectionToSingle extends AbstractBoundOperation.AbstractBoundFunction - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public CollectionToSingle( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } - - /** - * Specific {@link BoundFunction function} operating on a collection of elements returning a collection of elements. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ - class CollectionToCollection - extends - AbstractBoundOperation.AbstractBoundFunction - { - /** - * Create an instance of a bound function. - * - * @param src - * The type this function is bound to. - * @param target - * The type this function returns. - * @param name - * The fully qualified name - * @param args - * Key-value-pairs of function arguments names and their values - */ - public CollectionToCollection( - @Nonnull final Class src, - @Nonnull final Class target, - @Nonnull final String name, - @Nonnull final Map args ) - { - super(src, target, name, args); - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundOperation.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundOperation.java deleted file mode 100644 index db19d07b17..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundOperation.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -/** - * Interface representing OData operations (functions and actions) bound to a specific type. - * - * @param - * The type the function is bound to. - * @param - * The type this function returns. - */ -public interface BoundOperation -{ - /** - * The fully qualified name of the bound operation. - * - * @return The fully qualified name of bound operation. - */ - @Nonnull - String getQualifiedName(); - - /** - * The type this operations is bound to. - * - * @return The type this operations is bound to. - */ - @Nonnull - Class getBindingType(); - - /** - * The type this operations returns. - * - * @return The type this operations returns. - */ - @Nonnull - Class getReturnType(); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueActionRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueActionRequestBuilder.java deleted file mode 100644 index 1b4363fbda..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueActionRequestBuilder.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import lombok.AccessLevel; -import lombok.Getter; - -/** - * Representation of an OData action request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. This one handles actions where the return type is a collection of - * primitive or entity or complex type values. - * - * @param - * The type of the result entity, or primitive or complex type - */ -public class CollectionValueActionRequestBuilder - extends - ActionRequestBuilder, ActionResponseCollection> -{ - @Getter( AccessLevel.PROTECTED ) - private final Class resultClass; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param actionName - * The name of the unbound action - * @param resultClass - * The expected collection return type of the action. - */ - public CollectionValueActionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final String actionName, - @Nonnull final Class resultClass ) - { - super(servicePath, actionName); - this.resultClass = resultClass; - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param actionName - * The name of the unbound action - * @param parameters - * The parameters passed to the action. - * @param resultClass - * The expected collection return type of the action. - */ - public CollectionValueActionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final String actionName, - @Nonnull final Map parameters, - @Nonnull final Class resultClass ) - { - super(servicePath, actionName, parameters); - this.resultClass = resultClass; - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param actionPath - * The path to the unbound action - * @param parameters - * The parameters passed to the action. - * @param resultClass - * The expected collection return type of the action. - */ - public CollectionValueActionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath actionPath, - @Nonnull final Map parameters, - @Nonnull final Class resultClass ) - { - super(servicePath, actionPath, parameters); - this.resultClass = resultClass; - } - - @Nonnull - @Override - public ActionResponseCollection execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - - return ActionResponseCollection.of(response, resultClass); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueFunctionRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueFunctionRequestBuilder.java deleted file mode 100644 index 99c40f44b6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueFunctionRequestBuilder.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataFunctionParameters; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import lombok.AccessLevel; -import lombok.Getter; - -/** - * Representation of an OData function request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. This one handles functions where the return type is a collection of - * primitive or entity values. - * - * @param - * The type of the result entity, if any. - */ -public class CollectionValueFunctionRequestBuilder - extends - FunctionRequestBuilder, List> -{ - @Nonnull - @Getter( AccessLevel.PROTECTED ) - private final Class resultClass; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param functionName - * The name of the unbound function - * @param resultClass - * The expected collection return type of the function. - */ - public CollectionValueFunctionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final String functionName, - @Nonnull final Class resultClass ) - { - this(servicePath, functionName, Collections.emptyMap(), resultClass); - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param functionName - * The name of the unbound function - * @param parameters - * The parameters passed to the function. - * @param resultClass - * The expected collection return type of the function. - */ - public CollectionValueFunctionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final String functionName, - @Nonnull final Map parameters, - @Nonnull final Class resultClass ) - { - this( - servicePath, - ODataResourcePath.of(functionName, ODataFunctionParameters.of(parameters, ODataProtocol.V4)), - resultClass); - - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param functionPath - * The {@link ODataResourcePath} identifying the function to invoke. - * @param resultClass - * The expected collection return type of the function. - */ - CollectionValueFunctionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath functionPath, - @Nonnull final Class resultClass ) - { - super(servicePath, functionPath); - this.resultClass = resultClass; - } - - @Nonnull - @Override - public List execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - return response.asList(getResultClass()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ComplexProperty.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ComplexProperty.java deleted file mode 100644 index 93d0e12ef6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ComplexProperty.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableCollection; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableComplex; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * Interface representing a complex property of {@link EntityT} that holds a complex type of {@link TargetT}. - * - * @param - * Entity this property is part of. - * @param - * {@link VdmComplex} this property represents. - */ -public interface ComplexProperty, TargetT extends VdmComplex> - extends - StructuredProperty -{ - /** - * A collection of complex objects. - * - * @param - * The entity type. - * @param - * The complex type. - */ - @Getter - @RequiredArgsConstructor - class Collection, ValueT extends VdmComplex> - implements - ComplexProperty, - ProtocolQueryRead, - FilterableCollection - { - private final Class entityType; - private final String fieldName; - private final Class itemType; - - @Override - @Nonnull - @SafeVarargs - @SuppressWarnings( "varargs" ) - public final ComplexPropertyQuery select( @Nonnull final Property... fields ) - { - return ComplexPropertyQuery. onProperty(getFieldName()).select(fields); - } - } - - /** - * A navigational property to a single other entity reference. - * - * @param - * The entity type. - * @param - * The navigable entity type. - */ - @Getter - @RequiredArgsConstructor - class Single, ValueT extends VdmComplex> - implements - ComplexProperty, - ProtocolQueryRead, - FilterableComplex - { - private final Class entityType; - private final String fieldName; - private final Class itemType; - - @Override - @Nonnull - @SafeVarargs - @SuppressWarnings( "varargs" ) - public final ComplexPropertyQuery select( @Nonnull final Property... fields ) - { - return ComplexPropertyQuery. onProperty(getFieldName()).select(fields); - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ComplexPropertyQuery.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ComplexPropertyQuery.java deleted file mode 100644 index b8e49e7a09..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ComplexPropertyQuery.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Arrays; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.query.StructuredQuery; - -/** - * Implementation that represents read queries and holds their state at runtime. It allows for nested queries in a - * recursive manner. The implementation is the same for queries over single entities and collections of entities. In the - * VDM the available functionality is limited by the interfaces. - * - * In order to support a fluent creation of nested queries both the entity and the parent entity type are stored via - * generics. By implementing {@link AbstractStructuredPropertyQuery} the API doesn't differentiate between selections - * via referencing complex properties and selections via sub-queries. - * - * @param - * {@link VdmObject} this property is part of. - * @param - * {@link VdmComplex} type this property references to. - */ -public final class ComplexPropertyQuery, PropertyT extends VdmComplex> - extends - AbstractStructuredPropertyQuery -{ - private ComplexPropertyQuery( final StructuredQuery delegateQuery ) - { - super(delegateQuery); - } - - static < - ParentEntityT extends VdmObject, EntityT extends VdmComplex> - ComplexPropertyQuery - onProperty( @Nonnull final String fieldName ) - { - return new ComplexPropertyQuery<>(StructuredQuery.asNestedQueryOnProperty(fieldName, ODataProtocol.V4)); - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final ComplexPropertyQuery select( @Nonnull final Property... fields ) - { - super.select(Arrays.asList(fields)); - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CountRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CountRequestBuilder.java deleted file mode 100644 index 8194333527..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CountRequestBuilder.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCount; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableBoolean; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an OData request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. - * - * @param - * The generic entity type. - */ -@Slf4j -public class CountRequestBuilder> - extends - AbstractEntityBasedRequestBuilder, EntityT, Long> - implements - ProtocolQueryFilter -{ - private final NavigationPropertyCollectionQuery delegateQuery; - - @Getter( AccessLevel.PROTECTED ) - private final Class entityClass; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityClass - * The expected entity type. - * @param entityCollection - * The entity collection - */ - public CountRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final Class entityClass, - @Nonnull final String entityCollection ) - { - this(servicePath, ODataResourcePath.of(entityCollection), entityClass); - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param resourceToCount - * {@link ODataResourcePath} that identifies the collection to count. - * @param entityClass - * The expected entity type. - */ - @SuppressWarnings( "this-escape" ) - CountRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath resourceToCount, - @Nonnull final Class entityClass ) - { - super(servicePath, resourceToCount); - this.entityClass = entityClass; - this.delegateQuery = NavigationPropertyCollectionQuery.ofRootQuery(getResourcePath().toString()); - } - - /** - * Creates an instance of the {@link ODataRequestCount} based on the Entity class. - *

- * The following settings are used: - *

    - *
  • the endpoint URL
  • - *
  • the entity collection name
  • - *
  • the filters to be applied
  • - *
- * - * @return An initialized {@link ODataRequestCount}. - */ - @Override - @Nonnull - public ODataRequestCount toRequest() - { - final ODataRequestCount request = - new ODataRequestCount( - getServicePath(), - getResourcePath(), - delegateQuery.getEncodedQueryString(), - ODataProtocol.V4); - - return super.toRequest(request); - } - - @Override - @Nonnull - public Long execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - return response.as(Long.class); - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final CountRequestBuilder filter( @Nonnull final FilterableBoolean... filters ) - { - delegateQuery.filter(filters); - return this; - } - - @Override - @Nonnull - public CountRequestBuilder search( @Nonnull final String search ) - { - delegateQuery.search(search); - return this; - } - - @Override - @Nonnull - public CountRequestBuilder search( @Nonnull final SearchExpression expression ) - { - delegateQuery.search(expression); - return this; - } - - /** - * Activates CSRF token retrieval for this OData request. - * - * @return The same request builder that will now fetch a CSRF token. - * @deprecated CSRF token handling is now performed automatically by the underlying HTTP client. This method is a - * no-op retained only for source compatibility, as read requests never require a CSRF token. It is - * scheduled for removal. - */ - @Deprecated - @Nonnull - public CountRequestBuilder withCsrfToken() - { - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CreateRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CreateRequestBuilder.java deleted file mode 100644 index 5778fa51af..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/CreateRequestBuilder.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.google.gson.Gson; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataSerializationException; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCreate; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an OData request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. - * - * @param - * The type of the result entity. - */ -@Slf4j -public class CreateRequestBuilder> - extends - AbstractEntityBasedRequestBuilder, EntityT, ModificationResponse> - implements - ModificationRequestBuilder> -{ - /** - * Getter for the VDM representation of the entity to be created. - */ - @Nonnull - @Getter( AccessLevel.PROTECTED ) - private final EntityT entity; - - /** - * Instantiates a {@code CreateRequestBuilder}. - * - * @param servicePath - * The service path to direct the requests to. - * @param entity - * The entity to create. - * @param entityCollection - * The entity collection - */ - public CreateRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final EntityT entity, - @Nonnull final String entityCollection ) - { - this(servicePath, ODataResourcePath.of(entityCollection), entity); - } - - /** - * Instantiates a {@code CreateRequestBuilder}. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityPath - * {@link ODataResourcePath} identifying the collection the entity should be created into. - * @param entity - * The entity to create. - */ - CreateRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath entityPath, - @Nonnull final EntityT entity ) - { - super(servicePath, entityPath); - this.entity = entity; - } - - @SuppressWarnings( "unchecked" ) - @Nonnull - @Override - protected Class getEntityClass() - { - return (Class) getEntity().getClass(); - } - - /** - * Execute the OData create request for the provided entity. - * - * {@inheritDoc} - * - * @return The wrapped service response, exposing response headers, status code and entity references. If the HTTP - * response is not within healthy bounds, then one of the declared runtime exceptions will be thrown with - * further details. - */ - @Nonnull - @Override - public ModificationResponse execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - - return ModificationResponse.of(response, getEntity()); - } - - /** - * Creates an instance of the {@link ODataRequestCreate} based on the Entity class. - *

- * The following settings are used: - *

    - *
  • the endpoint URL
  • - *
  • the entity collection name
  • - *
  • the entity JSON payload
  • - *
- * - * @return An initialized {@link ODataRequestCreate}. - * @throws ODataSerializationException - * If entity cannot be serialized for HTTP request. - */ - @Override - @Nonnull - public ODataRequestCreate toRequest() - { - try { - final String serializedEntity = new Gson().toJson(entity); - - final ODataRequestCreate request = - new ODataRequestCreate(getServicePath(), getResourcePath(), serializedEntity, ODataProtocol.V4); - - return super.toRequest(request); - } - catch( final Exception e ) { - final String msg = "Failed to serialize HTTP request entity of type " + getEntityClass().getSimpleName(); - log.debug(msg, e); - final ODataRequestGeneric request = - new ODataRequestCreate(getServicePath(), getResourcePath(), "", ODataProtocol.V4); - throw new ODataSerializationException(request, entity, msg, e); - } - } - - /** - * Deactivates the CSRF token retrieval for this OData request. This is useful if the server does not support or - * require CSRF tokens as part of the request. - * - * @return The same builder - */ - @Override - @Nonnull - public CreateRequestBuilder withoutCsrfToken() - { - setSkipCsrfTokenRetrieval(); - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/DecimalDescriptor.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/DecimalDescriptor.java deleted file mode 100644 index ae2173351e..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/DecimalDescriptor.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation indicating the precision and scale of a decimal field - */ -@Retention( RetentionPolicy.RUNTIME ) -@Target( ElementType.FIELD ) -public @interface DecimalDescriptor { - - /** - * The associated precision of the decimal number - * - * @return The associated precision of the decimal number - */ - int precision(); - - /** - * The associated scale of the decimal number - * - * @return The associated scale of the decimal number - */ - int scale(); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/DeleteRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/DeleteRequestBuilder.java deleted file mode 100644 index d5cca1f9ca..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/DeleteRequestBuilder.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ETagSubmissionStrategy; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestDelete; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an OData delete request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. - * - * @param - * The type of the entity to delete. - */ -@Slf4j -public class DeleteRequestBuilder> - extends - AbstractEntityBasedRequestBuilder, EntityT, ModificationResponse> - implements - ModificationRequestBuilder> -{ - /** - * The entity object to be deleted by calling the {@link #execute(Destination)} method. - */ - @Nonnull - @Getter( AccessLevel.PROTECTED ) - private final EntityT entity; - - private ETagSubmissionStrategy eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_ETAG_FROM_ENTITY; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entity - * The entity to delete. - * @param entityCollection - * The entity collection - */ - public DeleteRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final EntityT entity, - @Nonnull final String entityCollection ) - { - this(servicePath, ODataResourcePath.of(entityCollection, entity.getKey()), entity); - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityPath - * The {@link ODataResourcePath} that identifies the entity to delete. - * @param entity - * The entity to delete. - */ - DeleteRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath entityPath, - @Nonnull final EntityT entity ) - { - super(servicePath, entityPath); - this.entity = entity; - } - - /** - * Execute the OData delete request for the provided entity. - * - * {@inheritDoc} - * - * @return The wrapped service response, exposing response headers, status code and entity references. If the HTTP - * response is not within healthy bounds, then one of the declared runtime exceptions will be thrown with - * further details. - */ - @Nonnull - @Override - public ModificationResponse execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - - return ModificationResponse.of(response, getEntity()); - } - - /** - * Creates an instance of the {@link ODataRequestDelete} based on the Entity class. - *

- * The following settings are used: - *

    - *
  • the endpoint URL
  • - *
  • the entity collection name
  • - *
  • the key fields of the entity
  • - *
- * - * @return An initialized {@link ODataRequestDelete}. - */ - @Override - @Nonnull - public ODataRequestDelete toRequest() - { - final String versionIdentifier = - eTagSubmissionStrategy.getHeaderFromVersionIdentifier(entity.getVersionIdentifier()); - - final ODataRequestDelete request = - new ODataRequestDelete(getServicePath(), getResourcePath(), versionIdentifier, ODataProtocol.V4); - - return super.toRequest(request); - } - - /** - * The delete request will ignore any version identifier present on the entity and not send an `If-Match` header. - *

- * Warning: This might lead to a response from the remote system that the `If-Match` header is missing. - *

- * It depends on the implementation of the remote system whether the `If-Match` header is expected. - * - * @return The same request builder that will not send the `If-Match` header in the delete request - */ - @Nonnull - public DeleteRequestBuilder disableVersionIdentifier() - { - eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_NO_ETAG; - return this; - } - - /** - * The delete request will ignore any version identifier present on the entity and delete the entity, regardless of - * any changes on the remote entity. - *

- * Warning: Be careful with this option, as this might overwrite any changes made to the remote - * representation of this object. - * - * @return The same request builder that will ignore the version identifier of the entity to delete - */ - @Nonnull - public DeleteRequestBuilder matchAnyVersionIdentifier() - { - eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_ANY_MATCH_ETAG; - return this; - } - - @SuppressWarnings( "unchecked" ) - @Nonnull - @Override - protected Class getEntityClass() - { - return (Class) entity.getClass(); - } - - @Nonnull - @Override - public DeleteRequestBuilder withoutCsrfToken() - { - setSkipCsrfTokenRetrieval(); - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/FunctionRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/FunctionRequestBuilder.java deleted file mode 100644 index 4ae0c1bb84..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/FunctionRequestBuilder.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestFunction; - -/** - * Representation of a non-CRUD OData request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. - * - * @param - * The request builder type. - * @param - * The type of the result entity, if any. - */ -public abstract class FunctionRequestBuilder, ResultT> - extends - AbstractRequestBuilder - implements - ReadRequestBuilder -{ - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param functionPath - * The {@link ODataResourcePath} identifying the function to invoke. - */ - public FunctionRequestBuilder( @Nonnull final String servicePath, @Nonnull final ODataResourcePath functionPath ) - { - super(servicePath, functionPath); - } - - /** - * Creates an instance of {@link ODataRequestFunction} based on the Entity class. - *

- * The following settings are used: - *

    - *
  • the endpoint URL
  • - *
  • the function name
  • - *
  • the parameters if applicable
  • - *
- * - * @return An initialized {@link ODataRequestFunction}. - */ - @Override - @Nonnull - public ODataRequestFunction toRequest() - { - final ODataRequestFunction request = - new ODataRequestFunction(getServicePath(), getResourcePath(), null, ODataProtocol.V4); - - return super.toRequest(request); - } - - /** - * Activates CSRF token retrieval for this OData request. - * - * @return The same request builder that will now fetch a CSRF token. - * @deprecated CSRF token handling is now performed automatically by the underlying HTTP client. This method is a - * no-op retained only for source compatibility. It is scheduled for removal. - */ - @Deprecated - @Override - @Nonnull - public BuilderT withCsrfToken() - { - return getThis(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/GetAllRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/GetAllRequestBuilder.java deleted file mode 100644 index 7c733d30ce..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/GetAllRequestBuilder.java +++ /dev/null @@ -1,273 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.List; -import java.util.stream.Stream; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Streams; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FieldOrdering; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableBoolean; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an OData request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. - * - * @param - * The type of the result entity. - */ -@Slf4j -public class GetAllRequestBuilder> - extends - AbstractEntityBasedRequestBuilder, EntityT, List> - implements - ProtocolQueryRead, - ProtocolQueryReadCollection, - ReadRequestBuilder> -{ - private final NavigationPropertyCollectionQuery delegateQuery; - - @Getter( AccessLevel.PROTECTED ) - @Nonnull - private final Class entityClass; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityClass - * The expected entity type. - * @param entityCollection - * The entity collection - */ - public GetAllRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final Class entityClass, - @Nonnull final String entityCollection ) - { - this(servicePath, ODataResourcePath.of(entityCollection), entityClass); - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityPath - * {@link ODataResourcePath} identifying the entity collection to read. - * @param entityClass - * The expected entity type. - */ - @SuppressWarnings( "this-escape" ) - GetAllRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath entityPath, - @Nonnull final Class entityClass ) - { - super(servicePath, entityPath); - this.entityClass = entityClass; - this.delegateQuery = NavigationPropertyCollectionQuery.ofRootQuery(getResourcePath().toString()); - } - - /** - * Creates an instance of the {@link ODataRequestRead} based on the Entity class. - *

- * The following settings are used: - *

    - *
  • the endpoint URL
  • - *
  • the entity collection name
  • - *
  • the number of entries to select (top)
  • - *
  • the number of entries to ignore (skip)
  • - *
  • the order direction
  • - *
  • the filters to be applied
  • - *
  • the fields to be selected
  • - *
- * - * @return An initialized {@link ODataRequestRead}. - */ - @Override - @Nonnull - public ODataRequestRead toRequest() - { - final ODataRequestRead request = - new ODataRequestRead( - getServicePath(), - getResourcePath(), - delegateQuery.getEncodedQueryString(), - ODataProtocol.V4); - - return super.toRequest(request); - } - - /** - * {@inheritDoc} - *

- * Note: If the OData service responds with service-driven pagination, then the pages will be - * iterated automatically. The returned list is an eagerly loaded aggregation of all pages. Access to a lazy loading - * result-set can be enabled through the modifiers {@link #iteratingEntities()}, {@link #streamingEntities()} and - * {@link #iteratingPages()}. - */ - @Override - @Nonnull - public List execute( @Nonnull final Destination destination ) - { - final Iterable iterableItems = iteratingEntities().execute(destination); - return Lists.newArrayList(iterableItems); // eagerly request and parse all pages of the result-set - } - - /** - * Manually explore the individual pages from the result-set. The returning object allows for memory-efficient - * consumption of all data through server-driven pagination. - * - * @return An instance of {@link RequestBuilderExecutable} with a response object to lazily iterate through the - * pages of entities. - */ - @Nonnull - public RequestBuilderExecutable>> iteratingPages() - { - return this::executeInternal; - } - - /** - * Iterate through all entities from the result-set. The individual pages of the result-set are queried lazily. The - * returning object allows for memory-efficient consumption of all data through server-driven pagination. - * - * @return An instance of {@link RequestBuilderExecutable} with a response object to lazily iterate through the - * entities. - */ - @Nonnull - public RequestBuilderExecutable> iteratingEntities() - { - // concat applies lazy evaluation so individual pages will still be loaded lazily - return destination -> Iterables.concat(executeInternal(destination)); - } - - /** - * Stream through all entities from the result-set. The individual pages of the result-set are queried lazily. The - * returning object allows for memory-efficient consumption of all data through server-driven pagination. - * - * @return An instance of {@link RequestBuilderExecutable} with a response object to lazily iterate through the - * entities. - */ - @Nonnull - public RequestBuilderExecutable> streamingEntities() - { - // concat applies lazy evaluation so individual pages will still be loaded lazily - return destination -> Streams.stream(Iterables.concat(executeInternal(destination))); - } - - /** - * Set the preferred page size of the OData response. A result-set may be split into multiple pages, each including - * a subset of the entities matching the query. - *

- * Note: The OData service might ignore the preferred page size setting and may not use pagination - * at all. - * - * @param size - * The preferred page size - * @return This request object with the added parameter. - */ - @Nonnull - public GetAllRequestBuilder withPreferredPageSize( final int size ) - { - return withHeader("Prefer", "odata.maxpagesize=" + size); - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( { "varargs" } ) - public final GetAllRequestBuilder select( @Nonnull final Property... fields ) - { - delegateQuery.select(fields); - return this; - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final GetAllRequestBuilder filter( @Nonnull final FilterableBoolean... filters ) - { - delegateQuery.filter(filters); - return this; - } - - @Override - @Nonnull - public GetAllRequestBuilder top( final int top ) - { - delegateQuery.top(top); - return this; - } - - @Override - @Nonnull - public GetAllRequestBuilder skip( final int skip ) - { - delegateQuery.skip(skip); - return this; - } - - @SafeVarargs - @Override - @Nonnull - @SuppressWarnings( "varargs" ) - public final GetAllRequestBuilder orderBy( @Nonnull final FieldOrdering... ordering ) - { - delegateQuery.orderBy(ordering); - return this; - } - - @Override - @Nonnull - public GetAllRequestBuilder search( @Nonnull final String search ) - { - delegateQuery.search(search); - return this; - } - - @Override - @Nonnull - public GetAllRequestBuilder search( @Nonnull final SearchExpression expression ) - { - delegateQuery.search(expression); - return this; - } - - @Nonnull - private Iterable> executeInternal( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - return toRequest().execute(httpClient).iteratePages(getEntityClass()); - } - - /** - * Activates CSRF token retrieval for this OData request. - * - * @return The same request builder that will now fetch a CSRF token. - * @deprecated CSRF token handling is now performed automatically by the underlying HTTP client. This method is a - * no-op retained only for source compatibility, as read requests never require a CSRF token. It is - * scheduled for removal. - */ - @Deprecated - @Override - @Nonnull - public GetAllRequestBuilder withCsrfToken() - { - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/GetByKeyRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/GetByKeyRequestBuilder.java deleted file mode 100644 index 122fdf5443..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/GetByKeyRequestBuilder.java +++ /dev/null @@ -1,171 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestReadByKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an OData request to retrieve an entity by its key as a fluent interface for further configuring the - * request and {@link #execute(Destination) executing} it. - * - * @param - * The type of the result entity. - */ -@Slf4j -public class GetByKeyRequestBuilder> - extends - AbstractEntityBasedRequestBuilder, EntityT, EntityT> - implements - ProtocolQueryRead, - ReadRequestBuilder -{ - private final NavigationPropertySingleQuery delegateQuery; - - @Getter( AccessLevel.PROTECTED ) - @Nonnull - private final Class entityClass; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityClass - * The expected entity type. - * @param entityKey - * The composite entity key. - * @param entityCollection - * The entity collection - * @throws IllegalArgumentException - * When there is no mapping found for one of the provided Java literal in the composite key. - */ - public GetByKeyRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final Class entityClass, - @Nonnull final Map entityKey, - @Nonnull final String entityCollection ) - { - this(servicePath, entityClass, ODataEntityKey.of(entityKey, ODataProtocol.V4), entityCollection); - } - - /** - * Instantiates a request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityClass - * The expected entity type. - * @param entityKey - * The composite entity key. - * @param entityCollection - * The entity collection - */ - GetByKeyRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final Class entityClass, - @Nonnull final ODataEntityKey entityKey, - @Nonnull final String entityCollection ) - { - this(servicePath, ODataResourcePath.of(entityCollection, entityKey), entityClass); - } - - /** - * Instantiates a request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param entityClass - * The expected entity type. - * @param entityPath - * The {@link ODataResourcePath} that identifies the entity to read. - */ - @SuppressWarnings( "this-escape" ) - GetByKeyRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath entityPath, - @Nonnull final Class entityClass ) - { - super(servicePath, entityPath); - this.entityClass = entityClass; - this.delegateQuery = NavigationPropertySingleQuery.ofRootQuery(getResourcePath().toString()); - } - - /** - * Creates an instance of {@link ODataRequestReadByKey} based on the Entity class. - *

- * The following settings are used: - *

    - *
  • the endpoint URL
  • - *
  • the entity collection name
  • - *
  • the key fields of the entity
  • - *
  • the fields to be selected
  • - *
- * - * @return An initialized {@link ODataRequestReadByKey}. - */ - @Override - @Nonnull - public ODataRequestReadByKey toRequest() - { - final ODataRequestReadByKey request = - new ODataRequestReadByKey( - getServicePath(), - getResourcePath(), - delegateQuery.getEncodedQueryString(), - ODataProtocol.V4); - - return super.toRequest(request); - } - - @Override - @Nonnull - public EntityT execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - final EntityT entity = response.as(getEntityClass()); - - response.getVersionIdentifierFromHeader().peek(entity::setVersionIdentifier); - - return entity; - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final GetByKeyRequestBuilder select( @Nonnull final Property... fields ) - { - delegateQuery.select(fields); - return this; - } - - /** - * Activates CSRF token retrieval for this OData request. - * - * @return The same request builder that will now fetch a CSRF token. - * @deprecated CSRF token handling is now performed automatically by the underlying HTTP client. This method is a - * no-op retained only for source compatibility, as read requests never require a CSRF token. It is - * scheduled for removal. - */ - @Deprecated - @Override - @Nonnull - public GetByKeyRequestBuilder withCsrfToken() - { - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ModificationRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ModificationRequestBuilder.java deleted file mode 100644 index 63876dfea5..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ModificationRequestBuilder.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -/** - * Interface to mark OData request types as modifying operation. - * - * @param - * The type of the request's result, if any. - * - * @see CreateRequestBuilder - * @see DeleteRequestBuilder - * @see UpdateRequestBuilder - * @see ActionRequestBuilder - */ -public interface ModificationRequestBuilder extends RequestBuilder -{ - /** - * Deactivates the CSRF token retrieval for this OData request. This is useful if the server does not support or - * require CSRF tokens as part of the request. - * - * @return The same builder - */ - @Nonnull - ModificationRequestBuilder withoutCsrfToken(); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ModificationResponse.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ModificationResponse.java deleted file mode 100644 index 945dc7b8fe..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ModificationResponse.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import io.vavr.control.Option; -import io.vavr.control.Try; -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -/** - * Generic OData service response wrapper for data modification requests. - * - * @param - * The generic entity type. - */ -@EqualsAndHashCode -@ToString -@RequiredArgsConstructor( staticName = "of", access = AccessLevel.PUBLIC ) -@Slf4j -public final class ModificationResponse> -{ - private static final Gson GSON = new Gson(); - - @Nullable - private EntityT modifiedEntity; - - @Nonnull - private Option responseEntity = Option.none(); - - @Nonnull - private final ODataRequestResultGeneric result; - - @Nonnull - private final EntityT originalRequestEntity; - - /** - * Get an updated version of the entity. If the service responded with an entity it is returned here. If the service - * didn't respond with an entity but send an @{code ETag} header, the entity is updated and returned. Otherwise a - * copy of the original, unmodified entity is returned. - * - * @return The modified entity. - */ - @Nonnull - public synchronized EntityT getModifiedEntity() - { - if( modifiedEntity == null ) { - evaluateResponse(); - } - return modifiedEntity; - } - - /** - * Get the optional response entity parsed from the HTTP content. - * - * @return The parsed entity or none. - */ - @Nonnull - public synchronized Option getResponseEntity() - { - // We synchronize the full method here because Fortify does not like double checked locking: - // https://vulncat.fortify.com/en/detail?id=desc.structural.java.code_correctness_double_checked_locking - if( modifiedEntity == null ) { - evaluateResponse(); - } - return responseEntity; - } - - @SuppressWarnings( "unchecked" ) - private void evaluateResponse() - { - responseEntity = parseEntityFromResponse(); - - // create a copy before modifying the version identifier to not change existing objects - final EntityT entityToModify = responseEntity.getOrElse(originalRequestEntity); - - // clone entity - modifiedEntity = GSON.fromJson(GSON.toJson(entityToModify), (Class) originalRequestEntity.getClass()); - - modifiedEntity.setVersionIdentifier(getUpdatedVersionIdentifier().getOrNull()); - } - - /** - * Access the original entity used to make the request. - * - * @return The original entity object used to perform an OData request. - */ - @Nonnull - public EntityT getRequestEntity() - { - return originalRequestEntity; - } - - /** - * Get the response status code. - * - * @return The integer representation of the HTTP status code. - */ - public int getResponseStatusCode() - { - return result.getHttpResponse().getCode(); - } - - /** - * Get the response headers. - * - * @return The headers of the HTTP status code. - */ - @Nonnull - public Map> getResponseHeaders() - { - return result.getAllHeaderValues(); - } - - /** - * Get the version identifier present in the response headers, the identifier of the original entity or none, if - * neither exist. - * - * @return An up to date version identifier or none. - */ - @Nonnull - public Option getUpdatedVersionIdentifier() - { - return result - .getVersionIdentifierFromHeader() - .orElse(() -> getResponseEntity().flatMap(entity -> entity.getVersionIdentifier())); - } - - @Nonnull - @SuppressWarnings( "unchecked" ) - private Option parseEntityFromResponse() - { - return Try - .of(() -> result.as((Class) originalRequestEntity.getClass())) - .onFailure(e -> log.debug("Failed to parse entity from HTTP response.", e)) - .toOption() - .peek(entity -> result.getVersionIdentifierFromHeader().peek(entity::setVersionIdentifier)); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigableEntityCollection.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigableEntityCollection.java deleted file mode 100644 index cc0f990e56..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigableEntityCollection.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -/** - * Related interface to provide access to type-safe request builders for the generic entity type. - * - * @param - * The generic entity type for which request builders can be instantiated. - */ -public interface NavigableEntityCollection> extends OperationsOnEntityCollections -{ - /** - * Fetch multiple entities. - * - * @return A request builder to fetch multiple entities. This request builder allows methods which modify the - * underlying query to be called before executing the query itself. To perform execution, call the - * {@link GetAllRequestBuilder#execute execute} method on the request builder object. - */ - @Nonnull - GetAllRequestBuilder getAll(); - - /** - * Create a new entity and save it to the OData service. - * - * @param item - * The entity object that will be created and saved. - *

- * Constraints: Not nullable - *

- * @return A request builder to create and save a new entity. To perform execution, call the - * {@link CreateRequestBuilder#execute execute} method on the request builder object. - */ - @Nonnull - CreateRequestBuilder create( @Nonnull final EntityT item ); - - /** - * Fetch the number of entries from the entity collection matching the filter and search expressions. - * - * @return A request builder to fetch the count of entities. This request builder allows methods which modify the - * underlying query to be called before executing the query itself. To perform execution, call the - * {@link CountRequestBuilder#execute execute} method on the request builder object. - */ - @Nonnull - CountRequestBuilder count(); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigableEntitySingle.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigableEntitySingle.java deleted file mode 100644 index 362c229484..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigableEntitySingle.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -/** - * Related interface to provide access to type-safe request builders for the generic entity type. - * - * @param - * The generic entity type for which request builders can be instantiated. - */ -public interface NavigableEntitySingle> -{ - /** - * Fetch the current entity. - * - * @return A request builder to fetch multiple entities. This request builder allows methods which modify the - * underlying query to be called before executing the query itself. To perform execution, call the - * {@link GetByKeyRequestBuilder#execute execute} method on the request builder object. - */ - @Nonnull - GetByKeyRequestBuilder get(); - - /** - * Update an existing entity and save it to the OData service. - * - * @param item - * The entity object that will be updated. - *

- * Constraints: Not nullable - *

- * @return A request builder to update an existing entity. To perform execution, call the - * {@link UpdateRequestBuilder#execute execute} method on the request builder object. - */ - @Nonnull - UpdateRequestBuilder update( @Nonnull final EntityT item ); - - /** - * Deletes the current entity in the OData service. - * - * @return A request builder to delete an existing entity. To perform execution, call the - * {@link DeleteRequestBuilder#execute execute} method on the request builder object. - */ - @Nonnull - DeleteRequestBuilder delete(); - - /** - * Navigate to a specific navigation property of current entity type. - * - * @param property - * The navigation property (collection) to be used. - * @param - * The generic type of target entity. - * @return A request builder to access further navigation properties or to instantiate type-safe request builders. - */ - @Nonnull - > NavigableEntityCollection navigateTo( - @Nonnull final NavigationProperty.Collection property ); - - /** - * Navigate to a specific navigation property of current entity type. - * - * @param property - * The navigation property (single) to be used. - * @param - * The generic type of target entity. - * @return A request builder to access further navigation properties or to instantiate type-safe request builders. - */ - @Nonnull - > NavigableEntitySingle navigateTo( - @Nonnull final NavigationProperty.Single property ); - - /** - * Apply a bound function returning a single object to the current element. - * - * @param function - * The function to apply. Functions are available on generated entity classes. - * @param - * The return type of the function. - * @return A new request builder for the supplied function applied to the current element. - */ - @Nonnull - SingleValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.SingleToSingle function ); - - /** - * Apply a bound function returning a collection of objects to the current element. - * - * @param function - * The function to apply. Functions are available on generated entity classes. - * @param - * The type of items in the result of the function. - * @return A new request builder for the supplied function applied to the current element. - */ - @Nonnull - CollectionValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.SingleToCollection function ); - - /** - * Use a composable function as a path element in an OData request. Similar to - * {@link #applyFunction(BoundFunction.SingleToSingle) applyFunction} but allows further path segments after the - * function. Functions must be marked as {@code composable} by the service. - * - * @param function - * The composable functions. - * @param - * The return type of the function. - * @return A request builder that allows for adding further path segments. - */ - @Nonnull - > NavigableEntitySingle withFunction( - @Nonnull final BoundFunction.SingleToSingleEntity.Composable function ); - - /** - * Use a composable function as a path element in an OData request. Similar to - * {@link #applyFunction(BoundFunction.SingleToSingle) applyFunction} but allows further path segments after the - * function. Functions must be marked as {@code composable} by the service. - * - * @param function - * The composable functions. - * @param - * The return type of the function. - * @return A request builder that allows for adding further path segments. - */ - @Nonnull - > NavigableEntityCollection withFunction( - @Nonnull final BoundFunction.SingleToCollectionEntity.Composable function ); - - /** - * Apply a bound action returning a single or no object to the current element. - * - * @param action - * The action to apply. Actions are available on generated entity classes. - * @param - * The return type of the action. - * @return A new request builder for the supplied action applied to the current element. - */ - @Nonnull - SingleValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.SingleToSingle action ); - - /** - * Apply a bound action returning a collection of elements to the current element. - * - * @param action - * The action to apply. Actions are available on generated entity classes. - * @param - * The return type of the action. - * @return A new request builder for the supplied action applied to the current element. - */ - @Nonnull - CollectionValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.SingleToCollection action ); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationProperty.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationProperty.java deleted file mode 100644 index 62063b8c3a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationProperty.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableCollection; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableComplex; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * Interface representing a navigational property of {@link EntityT} that points towards an entity of {@link TargetT}. - * - * @param - * Entity this property is part of. - * @param - * Entity this property references. - */ -public interface NavigationProperty, TargetT extends VdmEntity> - extends - StructuredProperty -{ - /** - * A navigational property to a Collection of another entity reference. - * - * @param - * The entity type. - * @param - * The navigable entity type. - */ - @Getter - @RequiredArgsConstructor - class Collection, ValueT extends VdmEntity> - extends - NavigationPropertyCollection - implements - NavigationProperty, - FilterableCollection - { - private final Class entityType; - private final String fieldName; - private final Class itemType; - } - - /** - * A navigational property to a single other entity reference. - * - * @param - * The entity type. - * @param - * The navigable entity type. - */ - @Getter - @RequiredArgsConstructor - class Single, ValueT extends VdmEntity> - implements - NavigationProperty, - ProtocolQueryRead, - FilterableComplex - { - private final Class entityType; - private final String fieldName; - private final Class itemType; - - @Override - @Nonnull - @SafeVarargs - @SuppressWarnings( "varargs" ) - public final NavigationPropertySingleQuery select( @Nonnull final Property... fields ) - { - return NavigationPropertySingleQuery. ofSubQuery(getFieldName()).select(fields); - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationPropertyCollection.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationPropertyCollection.java deleted file mode 100644 index 1f99f9bbc6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationPropertyCollection.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odatav4.expression.FieldOrdering; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableBoolean; - -/** - * Abstract class representing the query capabilities of a collection of structured properties of {@link EntityT} with a - * type of {@link TargetT}. - * - * @param - * Entity this property is part of. - * @param - * Entity type of the collection this property references to. - */ -public abstract class NavigationPropertyCollection, TargetT extends VdmEntity> - implements - StructuredProperty, - ProtocolQueryReadCollection, - ProtocolQueryRead -{ - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final NavigationPropertyCollectionQuery select( - @Nonnull final Property... fields ) - { - return NavigationPropertyCollectionQuery. ofSubQuery(getFieldName()).select(fields); - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final NavigationPropertyCollectionQuery filter( - @Nonnull final FilterableBoolean... filters ) - { - return NavigationPropertyCollectionQuery. ofSubQuery(getFieldName()).filter(filters); - } - - @Override - @Nonnull - public NavigationPropertyCollectionQuery top( final int top ) - { - return NavigationPropertyCollectionQuery. ofSubQuery(getFieldName()).top(top); - } - - @Override - @Nonnull - public NavigationPropertyCollectionQuery skip( final int skip ) - { - return NavigationPropertyCollectionQuery. ofSubQuery(getFieldName()).skip(skip); - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final NavigationPropertyCollectionQuery orderBy( - @Nonnull final FieldOrdering... ordering ) - { - return NavigationPropertyCollectionQuery. ofSubQuery(getFieldName()).orderBy(ordering); - } - - @Override - @Nonnull - public NavigationPropertyCollectionQuery search( @Nonnull final String search ) - { - return NavigationPropertyCollectionQuery. ofSubQuery(getFieldName()).search(search); - } - - @Override - @Nonnull - public NavigationPropertyCollectionQuery search( @Nonnull final SearchExpression expression ) - { - return NavigationPropertyCollectionQuery. ofSubQuery(getFieldName()).search(expression); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationPropertyCollectionQuery.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationPropertyCollectionQuery.java deleted file mode 100644 index 69669a66fc..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationPropertyCollectionQuery.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.OrderExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; -import com.sap.cloud.sdk.datamodel.odata.client.query.StructuredQuery; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FieldOrdering; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableBoolean; - -/** - * Implementation that represents read queries and holds their state at runtime. It allows for nested queries in a - * recursive manner. The implementation is the same for queries over single entities and collections of entities. In the - * VDM the available functionality is limited by the interfaces. - * - * In order to support a fluent creation of nested queries both the entity and the parent entity type are stored via - * generics. By implementing {@link ProtocolQueryReadCollection} the API doesn't differentiate between selections via - * referencing navigational properties and selections via sub-queries. - * - * @param - * The generic navigation property entity source type. - * @param - * The generic navigation property entity target type. - */ -public final class NavigationPropertyCollectionQuery, EntityT extends VdmEntity> - extends - AbstractStructuredPropertyQuery - implements - ProtocolQueryReadCollection -{ - private NavigationPropertyCollectionQuery( final StructuredQuery delegateQuery ) - { - super(delegateQuery); - } - - static < - ParentEntityT extends VdmObject, EntityT extends VdmEntity> - NavigationPropertyCollectionQuery - ofRootQuery( @Nonnull final String fieldName ) - { - return new NavigationPropertyCollectionQuery<>(StructuredQuery.onEntity(fieldName, ODataProtocol.V4)); - } - - static < - ParentEntityT extends VdmObject, EntityT extends VdmEntity> - NavigationPropertyCollectionQuery - ofSubQuery( @Nonnull final String fieldName ) - { - return new NavigationPropertyCollectionQuery<>( - StructuredQuery.asNestedQueryOnProperty(fieldName, ODataProtocol.V4)); - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final NavigationPropertyCollectionQuery select( - @Nonnull final Property... fields ) - { - super.select(Arrays.asList(fields)); - return this; - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final NavigationPropertyCollectionQuery filter( - @Nonnull final FilterableBoolean... filters ) - { - final Collection untypedFilters = new ArrayList<>(); - for( final FilterableBoolean fb : filters ) { - untypedFilters.add(fb::getExpression); - } - untypedFilters.forEach(delegateQuery::filter); - return this; - } - - @Override - @Nonnull - public NavigationPropertyCollectionQuery top( final int top ) - { - delegateQuery.top(top); - return this; - } - - @Override - @Nonnull - public NavigationPropertyCollectionQuery skip( final int skip ) - { - delegateQuery.skip(skip); - return this; - } - - @SafeVarargs - @Override - @Nonnull - public final NavigationPropertyCollectionQuery orderBy( - @Nonnull final FieldOrdering... ordering ) - { - final OrderExpression expression = FieldOrdering.toOrderExpression(ordering); - if( expression != null ) { - delegateQuery.orderBy(expression); - } - return this; - } - - @Nonnull - @Override - public NavigationPropertyCollectionQuery search( @Nonnull final String search ) - { - delegateQuery.search(SearchExpression.getDoubleQuotedString(search)); - return this; - } - - @Nonnull - @Override - public NavigationPropertyCollectionQuery search( - @Nonnull final SearchExpression expression ) - { - delegateQuery.search(expression.getTerm()); - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationPropertySingleQuery.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationPropertySingleQuery.java deleted file mode 100644 index 32e4dc87f0..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/NavigationPropertySingleQuery.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Arrays; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.query.StructuredQuery; - -/** - * Implementation that represents read queries and holds their state at runtime. It allows for nested queries in a - * recursive manner. The implementation is the same for queries over single entities and collections of entities. In the - * VDM the available functionality is limited by the interfaces. - * - * In order to support a fluent creation of nested queries both the entity and the parent entity type are stored via - * generics. By implementing {@link NavigationProperty} the API doesn't differentiate between selections via referencing - * navigational properties and selections via sub-queries. - * - * @param - * The generic navigation property entity source type. - * @param - * The generic navigation property entity target type. - */ -public final class NavigationPropertySingleQuery, EntityT extends VdmEntity> - extends - AbstractStructuredPropertyQuery -{ - private NavigationPropertySingleQuery( final StructuredQuery delegateQuery ) - { - super(delegateQuery); - } - - static < - ParentEntityT extends VdmObject, EntityT extends VdmEntity> - NavigationPropertySingleQuery - ofRootQuery( @Nonnull final String fieldName ) - { - return new NavigationPropertySingleQuery<>(StructuredQuery.onEntity(fieldName, ODataProtocol.V4)); - } - - static < - ParentEntityT extends VdmObject, EntityT extends VdmEntity> - NavigationPropertySingleQuery - ofSubQuery( @Nonnull final String fieldName ) - { - return new NavigationPropertySingleQuery<>( - StructuredQuery.asNestedQueryOnProperty(fieldName, ODataProtocol.V4)); - } - - @Override - @SafeVarargs - @Nonnull - @SuppressWarnings( "varargs" ) - public final NavigationPropertySingleQuery select( - @Nonnull final Property... fields ) - { - super.select(Arrays.asList(fields)); - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/OperationsOnEntityCollections.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/OperationsOnEntityCollections.java deleted file mode 100644 index 85522a1835..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/OperationsOnEntityCollections.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -/** - * Invoke actions and functions on OData collections - */ -public interface OperationsOnEntityCollections -{ - /** - * Create a generic type-safe request using navigation properties on entity sets of the OData service. - * - * @param entity - * A template entity instance that contains all necessary key attributes. - *

- * Constraints: Not nullable - *

- * @param - * The generic entity type for which navigation properties can be accessed. - * @return A request builder to navigate the service request using the provided key fields of an entity. With - * another navigation property this object can be used to instantiate new request builders. - */ - @Nonnull - > NavigableEntitySingle forEntity( @Nonnull final EntityT entity ); - - /** - * Use a composable function as a path element in an OData request. Similar to - * {@link #applyFunction(BoundFunction.CollectionToSingle) applyFunction} but allows further path segments after the - * function. Functions must be marked as {@code composable} by the service. - * - * @param function - * The composable functions. - * @param - * The entity type - * @param - * The return type of the function. - * @return A request builder that allows for adding further path segments. - */ - @Nonnull - < - EntityT extends VdmEntity, ResultT extends VdmEntity> - NavigableEntitySingle - - withFunction( @Nonnull final BoundFunction.CollectionToSingleEntity.Composable function ); - - /** - * Use a composable function as a path element in an OData request. Similar to - * {@link #applyFunction(BoundFunction.CollectionToSingle) applyFunction} but allows further path segments after the - * function. Functions must be marked as {@code composable} by the service. - * - * @param function - * The composable functions. - * @param - * The entity type - * @param - * The return type of the function. - * @return A request builder that allows for adding further path segments. - */ - @Nonnull - - < - EntityT extends VdmEntity, ResultT extends VdmEntity> - NavigableEntityCollection - withFunction( @Nonnull final BoundFunction.CollectionToCollectionEntity.Composable function ); - - /** - * Apply a bound action returning a collection of objects to the current entity collection. - * - * @param action - * The action to apply. Actions are available on generated entity classes. - * @param - * The entity type - * @param - * The return type of the action. - * @return A new request builder for the supplied action applied to the current elements. - */ - @Nonnull - , ResultT> CollectionValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.CollectionToCollection action ); - - /** - * Apply a bound action returning a single or no object to an entity collection of the service. - * - * @param action - * The action to apply. Actions are available on generated entity classes. - * @param - * The type this function is bound to - * @param - * The return type of the action. - * @return A new request builder for the supplied action applied to the current elements. - */ - @Nonnull - , ResultT> SingleValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.CollectionToSingle action ); - - /** - * Apply a bound function returning a single object to the current element. - * - * @param function - * The function to apply. Functions are available on generated entity classes. - * @param - * The type this function is bound to - * @param - * The return type of the function. - * @return A new request builder for the supplied function applied to the current element. - */ - @Nonnull - , ResultT> SingleValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.CollectionToSingle function ); - - /** - * Apply a bound function returning a collection of objects to the current element. - * - * @param function - * The function to apply. Functions are available on generated entity classes. - * @param - * The type this function is bound to - * @param - * The type of items in the result of the function. - * @return A new request builder for the supplied function applied to the current element. - */ - @Nonnull - , ResultT> CollectionValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.CollectionToCollection function ); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/Property.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/Property.java deleted file mode 100644 index 617cbee905..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/Property.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; - -/** - * Generic entity property. - * - * @param - * The generic entity type for this property. - */ -public interface Property extends FieldReference -{ -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ProtocolQueryFilter.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ProtocolQueryFilter.java deleted file mode 100644 index 9efc3026df..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ProtocolQueryFilter.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableBoolean; - -/** - * Interface that allows for constructing filterable OData queries (reading) over a collection of entities. - * - * @param - * The entity type to be queried. - */ -interface ProtocolQueryFilter> -{ - /** - * Filter on properties of {@linkplain EntityT}. - * - * @param filters - * Filter expressions to be added to the request. - * @return This request object with the added filters. - */ - @Nonnull - @SuppressWarnings( { "varargs", "unchecked" } ) - ProtocolQueryFilter filter( @Nonnull final FilterableBoolean... filters ); - - /** - * Request modifier to return the set of entities that contain the specified value. If this method is never called, - * then all the accessible entities will be returned. - * - * @param search - * A string value as the search criteria. - * @return The same request builder with this request modifier applied. - */ - @Nonnull - ProtocolQueryFilter search( @Nonnull final String search ); - - /** - * Request modifier to return the set of entities corresponding to the specified boolean expression. If this method - * is never called, then all the accessible entities will be returned. - * - * @param expression - * SearchExpression as the search criteria - * @return The same request builder with this request modifier applied. - */ - @Nonnull - ProtocolQueryFilter search( @Nonnull final SearchExpression expression ); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ProtocolQueryRead.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ProtocolQueryRead.java deleted file mode 100644 index 8e6210059b..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ProtocolQueryRead.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -/** - * Interface that allows for constructing OData read queries. - * - * @param - * The entity type to be queried. - */ -interface ProtocolQueryRead> -{ - /** - * Query modifier to limit which field values of the entity {@linkplain EntityT} get fetched and populated. - * Navigational properties supplied here will be expanded. If this method is called at least once, then only the - * specified fields will be fetched and populated. - * - * If none of the select methods is called, then all fields will be fetched and populated. Calling this multiple - * times will combine the set(s) of fields of each call. - * - * @param fields - * Properties of {@linkplain EntityT} to be selected. - * @return This request object with the added selections. - */ - @Nonnull - @SuppressWarnings( { "varargs", "unchecked" } ) - ProtocolQueryRead select( @Nonnull final Property... fields ); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ProtocolQueryReadCollection.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ProtocolQueryReadCollection.java deleted file mode 100644 index e7ddf4f34c..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ProtocolQueryReadCollection.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odatav4.expression.FieldOrdering; - -/** - * Interface that allows for constructing OData queries (reading) over a collection of entities. - * - * @param - * The entity type to be queried. - */ -interface ProtocolQueryReadCollection> extends ProtocolQueryFilter -{ - /** - * Limit the number of results of this request. - * - * @param top - * The maximum amount of elements this request shall return. - * @return This request with the top parameter set. - */ - @Nonnull - ProtocolQueryReadCollection top( int top ); - - /** - * Determine the how many first N entities of the result set should be skipped. If this method is never called, then - * the full list will be returned from the first entity. If this method is called multiple times, then only the - * value of the last call will be used. - * - * @param skip - * The amount of elements this request will skip. - * @return This request with the skip parameter set. - */ - @Nonnull - ProtocolQueryReadCollection skip( int skip ); - - /** - * Sort the set of returned entities by non-complex fields. - * - * @param ordering - * Fields to sort by. - * @return This request with the orderBy parameter set. - */ - @Nonnull - @SuppressWarnings( { "varargs", "unchecked" } ) - ProtocolQueryReadCollection orderBy( @Nonnull final FieldOrdering... ordering ); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ReadRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ReadRequestBuilder.java deleted file mode 100644 index 8276f9b9b5..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ReadRequestBuilder.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -/** - * Interface to mark OData request types as reading operation. - * - * @param - * The type of the request's result, if any. - * - * @see GetAllRequestBuilder - * @see GetByKeyRequestBuilder - * @see FunctionRequestBuilder - */ -public interface ReadRequestBuilder extends RequestBuilder -{ - /** - * Activates CSRF token retrieval for this OData request. - * - * @return The same request builder that will now fetch a CSRF token. - * @deprecated CSRF token handling is now performed automatically by the underlying HTTP client. This method is a - * no-op retained only for source compatibility, as read requests never require a CSRF token. It is - * scheduled for removal. - */ - @Deprecated - @Nonnull - default ReadRequestBuilder withCsrfToken() - { - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/RequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/RequestBuilder.java deleted file mode 100644 index 01ba7dae09..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/RequestBuilder.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; - -/** - * Representation of a generic OData request builder as a fluent interface. - * - * @param - * The type of the result entity, if any. - */ -public interface RequestBuilder extends RequestBuilderExecutable -{ - /** - * Assemble a generic, untyped request object that represents the request build up via this builder. - * - * @return A request object extending {@link ODataRequestGeneric}. - */ - @Nonnull - ODataRequestGeneric toRequest(); - - /** - * Gives the option to specify custom HTTP headers. Multiple headers with the same key can be specified. The - * returned object allows to specify the requests the headers should be used in. - * - * @param key - * Name of the (first) desired HTTP header parameter. - * @param value - * Value of the (first) desired HTTP header parameter. - * - * @return A request builder to specify further headers and their intended usage. - */ - @Nonnull - RequestBuilder withHeader( @Nonnull final String key, @Nullable final String value ); - - /** - * Gives the option to specify a map of custom HTTP headers. The returned object allows to specify the requests the - * headers should be used in. - * - * @param map - * A map of HTTP header key/value pairs. - * @return A request builder to specify further headers and their intended usage. - */ - @Nonnull - default RequestBuilder withHeaders( @Nonnull final Map map ) - { - map.forEach(this::withHeader); - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/RequestBuilderExecutable.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/RequestBuilderExecutable.java deleted file mode 100644 index 490465ffbd..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/RequestBuilderExecutable.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException; -import com.sap.cloud.sdk.cloudplatform.connectivity.exception.HttpClientInstantiationException; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException; - -import io.vavr.control.Try; - -/** - * Representation of a generic executable OData request builder as a fluent interface. - * - * @param - * The type of the result entity, if any. - */ -public interface RequestBuilderExecutable -{ - /** - * Execute the OData request. - * - * @param destination - * The destination to be used as request target. - * @return The generic OData response result. - * - * @throws DestinationAccessException - * If there is an issue accessing the {@link Destination}. - * @throws HttpClientInstantiationException - * If there is an issue creating the {@link HttpClient}. - * @throws ODataException - * If the OData request execution failed. Please find the documentation for {@link ODataException} - * possible sub-types and error scenarios they can occur in. - */ - @Nonnull - ResultT execute( @Nonnull final Destination destination ); - - /** - * Safely execute the OData request. - * - * @param destination - * The destination to be used as request target. - * @return The generic OData response result wrapped in a {@code Try} block. - */ - @Nonnull - default Try tryExecute( @Nonnull final Destination destination ) - { - return Try.of(() -> execute(destination)); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ResourcePathUtil.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ResourcePathUtil.java deleted file mode 100644 index 8f44a4cbea..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ResourcePathUtil.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; - -class ResourcePathUtil -{ - @Nonnull - static , ResultT> ODataResourcePath ofBoundOperation( - @Nonnull final BoundOperation operation ) - { - final String entityCollection = - new VdmEntityUtil<>(operation.getBindingType()).newInstance().getEntityCollection(); - - return new ODataResourcePath().addSegment(entityCollection); - } - - @Nonnull - static > ODataResourcePath ofEntity( @Nonnull final EntityT entity ) - { - return new ODataResourcePath().addSegment(entity.getEntityCollection(), entity.getKey()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SearchExpression.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SearchExpression.java deleted file mode 100644 index 1a3b8b3163..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SearchExpression.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * Representation of a OData query parameter for Search Modifier - */ -@RequiredArgsConstructor -public class SearchExpression -{ - @Nonnull - @Getter( AccessLevel.PROTECTED ) - private final String term; - - /** - * Create a search expression for a single string. - * - * @param term - * The search string - * @return Search Expression with a single search string. - */ - @Nonnull - public static SearchExpression of( @Nonnull final String term ) - { - return new SearchExpression(getDoubleQuotedString(term)); - } - - /** - * Combine current string with another search string in conjunction. - * - * @param term - * The other search string. - * @return Search Expression with a conjunction string. - */ - @Nonnull - public SearchExpression and( @Nonnull final String term ) - { - return new SearchExpression("(" + this.term + " AND " + getDoubleQuotedString(term) + ")"); - } - - /** - * Combine current search expression with another search expression in conjunction. - * - * @param searchExpression - * The other search expression. - * @return Search Expression with a conjunction. - */ - @Nonnull - public SearchExpression and( @Nonnull final SearchExpression searchExpression ) - { - return new SearchExpression("(" + this.term + " AND " + searchExpression.getTerm() + ")"); - } - - /** - * Combine current string with another search string in disjunction. - * - * @param term - * The other search string. - * @return Search Expression with a disjunction string. - */ - @Nonnull - public SearchExpression or( @Nonnull final String term ) - { - return new SearchExpression("(" + this.term + " OR " + getDoubleQuotedString(term) + ")"); - } - - /** - * Combine current search expression with another search expression in disjunction. - * - * @param searchExpression - * The other search expression. - * @return Search Expression with a disjunction. - */ - @Nonnull - public SearchExpression or( @Nonnull final SearchExpression searchExpression ) - { - return new SearchExpression("(" + this.term + " OR " + searchExpression.getTerm() + ")"); - } - - /** - * Negate the current search expression. - * - * @return Modified search expression with negation. - */ - @Nonnull - public SearchExpression not() - { - return new SearchExpression("NOT " + this.term); - } - - /** - * Escape and encapsulate String literal. - * - * @param text - * The String to be quoted. - * @return The prepared String. - */ - @Nonnull - static String getDoubleQuotedString( @Nonnull final String text ) - { - if( text.contains("&") ) { - throw new IllegalArgumentException("Search literal contains a forbidden character '&'."); - } - - // escape backslash \ -> \\ - String encodedText = text.replaceAll("\\\\", "$0$0"); - - // escape double quotes " -> \" - encodedText = encodedText.replaceAll("\"", "\\\\$0"); - - // wrap escaped string into double quotes - return "\"" + encodedText + "\""; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ServiceWithNavigableEntities.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ServiceWithNavigableEntities.java deleted file mode 100644 index 3469a6424c..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ServiceWithNavigableEntities.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; - -import io.vavr.control.Option; - -/** - * OData Service interface to provide type-safe and recursive access to nested entities and their navigation properties. - */ -public interface ServiceWithNavigableEntities extends OperationsOnEntityCollections -{ - /** - * Getter for the OData service root path. - * - * @return the service path. - */ - @Nonnull - String getServicePath(); - - /** - * Create a generic type-safe request using navigation properties on entity sets of the OData service. - * - * @param entity - * A template entity instance that contains all necessary key attributes. - *

- * Constraints: Not nullable - *

- * @param - * The generic entity type for which navigation properties can be accessed. - * @return A request builder to navigate the service request using the provided key fields of an entity. With - * another navigation property this object can be used to instantiate new request builders. - */ - @Override - @Nonnull - default < - EntityT extends VdmEntity> NavigableEntitySingle forEntity( @Nonnull final EntityT entity ) - { - if( !VdmEntitySet.class.isAssignableFrom(entity.getType()) ) { - throw new IllegalStateException( - "Entity type " - + entity.getType().getSimpleName() - + " must be a sub class of " - + VdmEntitySet.class.getName()); - } - - final ODataResourcePath resourcePath = ResourcePathUtil.ofEntity(entity); - - return new ServiceWithNavigableEntitiesImpl.EntitySingle<>( - getServicePath(), - resourcePath, - Option.of(entity), - entity.getType()); - } - - /** - * Use a composable function as a path element in an OData request. Similar to - * {@link #applyFunction(BoundFunction.CollectionToSingle) applyFunction} but allows further path segments after the - * function. Functions must be marked as {@code composable} by the service. - * - * @param function - * The composable functions. - * @param - * The entity type - * @param - * The return type of the function. - * @return A request builder that allows for adding further path segments. - */ - @Override - @Nonnull - default < - EntityT extends VdmEntity, ResultT extends VdmEntity> - NavigableEntitySingle - withFunction( @Nonnull final BoundFunction.CollectionToSingleEntity.Composable function ) - { - final ODataResourcePath resourcePath = - ResourcePathUtil - .ofBoundOperation(function) - .addSegment(function.getQualifiedName(), function.getParameters()); - - return new ServiceWithNavigableEntitiesImpl.EntitySingle<>( - getServicePath(), - resourcePath, - Option.none(), - function.getReturnType()); - } - - /** - * Use a composable function as a path element in an OData request. Similar to - * {@link #applyFunction(BoundFunction.CollectionToSingle) applyFunction} but allows further path segments after the - * function. Functions must be marked as {@code composable} by the service. - * - * @param function - * The composable functions. - * @param - * The entity type - * @param - * The return type of the function. - * @return A request builder that allows for adding further path segments. - */ - @Override - @Nonnull - default < - EntityT extends VdmEntity, ResultT extends VdmEntity> - NavigableEntityCollection - withFunction( @Nonnull final BoundFunction.CollectionToCollectionEntity.Composable function ) - { - final ODataResourcePath resourcePath = - ResourcePathUtil - .ofBoundOperation(function) - .addSegment(function.getQualifiedName(), function.getParameters()); - - return new ServiceWithNavigableEntitiesImpl.EntityCollection<>( - getServicePath(), - resourcePath, - function.getReturnType()); - } - - /** - * Apply a bound action returning a collection of objects to the current entity collection. - * - * @param action - * The action to apply. Actions are available on generated entity classes. - * @param - * The entity type - * @param - * The return type of the action. - * @return A new request builder for the supplied action applied to the current elements. - */ - @Override - @Nonnull - default , ResultT> CollectionValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.CollectionToCollection action ) - { - final ODataResourcePath resourcePath = ResourcePathUtil.ofBoundOperation(action); - - return new ServiceWithNavigableEntitiesImpl.EntityCollection<>( - getServicePath(), - resourcePath, - action.getBindingType()).applyAction(action); - } - - /** - * Apply a bound action returning a single or no object to an entity collection of the service. - * - * @param action - * The action to apply. Actions are available on generated entity classes. - * @param - * The type this function is bound to - * @param - * The return type of the action. - * @return A new request builder for the supplied action applied to the current elements. - */ - @Override - @Nonnull - default , ResultT> SingleValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.CollectionToSingle action ) - { - final ODataResourcePath resourcePath = ResourcePathUtil.ofBoundOperation(action); - - return new ServiceWithNavigableEntitiesImpl.EntityCollection<>( - getServicePath(), - resourcePath, - action.getBindingType()).applyAction(action); - } - - /** - * Apply a bound function returning a single object to the current element. - * - * @param function - * The function to apply. Functions are available on generated entity classes. - * @param - * The type this function is bound to - * @param - * The return type of the function. - * @return A new request builder for the supplied function applied to the current element. - */ - @Override - @Nonnull - default , ResultT> SingleValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.CollectionToSingle function ) - { - final ODataResourcePath resourcePath = ResourcePathUtil.ofBoundOperation(function); - - return new ServiceWithNavigableEntitiesImpl.EntityCollection<>( - getServicePath(), - resourcePath, - function.getBindingType()).applyFunction(function); - } - - /** - * Apply a bound function returning a collection of objects to the current element. - * - * @param function - * The function to apply. Functions are available on generated entity classes. - * @param - * The type this function is bound to - * @param - * The type of items in the result of the function. - * @return A new request builder for the supplied function applied to the current element. - */ - @Override - @Nonnull - default , ResultT> CollectionValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.CollectionToCollection function ) - { - final ODataResourcePath resourcePath = ResourcePathUtil.ofBoundOperation(function); - - return new ServiceWithNavigableEntitiesImpl.EntityCollection<>( - getServicePath(), - resourcePath, - function.getBindingType()).applyFunction(function); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ServiceWithNavigableEntitiesImpl.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ServiceWithNavigableEntitiesImpl.java deleted file mode 100644 index 76f4eca02c..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/ServiceWithNavigableEntitiesImpl.java +++ /dev/null @@ -1,308 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; - -import org.apache.hc.core5.http.HttpHeaders; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; - -import io.vavr.control.Option; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -class ServiceWithNavigableEntitiesImpl -{ - @RequiredArgsConstructor - static class EntitySingle> implements NavigableEntitySingle - { - /** - * The service path of the entity. - */ - @Nonnull - protected final String servicePath; - - /** - * The path to the entity. - */ - @Nonnull - protected final ODataResourcePath entityPath; - - /** - * The entity to be navigated. - */ - @Nonnull - protected final Option maybeEntity; - - @Nonnull - private final Class entityType; - - @Nonnull - @Override - public GetByKeyRequestBuilder get() - { - return new GetByKeyRequestBuilder<>(servicePath, entityPath, entityType); - } - - @Nonnull - @Override - public UpdateRequestBuilder update( @Nonnull final EntityT item ) - { - return new UpdateRequestBuilder<>(servicePath, entityPath, item); - } - - @Nonnull - @Override - public DeleteRequestBuilder delete() - { - return new DeleteRequestBuilder<>( - servicePath, - entityPath, - maybeEntity.getOrElse(() -> new VdmEntityUtil<>(entityType).newInstance())); - } - - @Nonnull - @Override - public > NavigableEntityCollection navigateTo( - @Nonnull final NavigationProperty.Collection property ) - { - return new EntityCollection<>( - servicePath, - entityPath.copy().addSegment(property.getFieldName()), - property.getItemType()); - } - - @Nonnull - @Override - public > NavigableEntitySingle navigateTo( - @Nonnull final NavigationProperty.Single property ) - { - return new EntitySingle<>( - servicePath, - entityPath.copy().addSegment(property.getFieldName()), - Option.none(), - property.getItemType()); - } - - @Nonnull - @Override - public SingleValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.SingleToSingle function ) - { - return newSingleValueFunctionRequestBuilder(servicePath, entityPath, function); - } - - @Nonnull - @Override - public CollectionValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.SingleToCollection function ) - { - return newCollectionValueFunctionRequestBuilder(servicePath, entityPath, function); - } - - @Override - @Nonnull - public > NavigableEntitySingle withFunction( - @Nonnull final BoundFunction.SingleToSingleEntity.Composable function ) - { - return new EntitySingle<>( - servicePath, - entityPath.copy().addSegment(function.getQualifiedName(), function.getParameters()), - Option.none(), - function.getReturnType()); - } - - @Override - @Nonnull - public > NavigableEntityCollection withFunction( - @Nonnull final BoundFunction.SingleToCollectionEntity.Composable function ) - { - return new EntityCollection<>( - servicePath, - entityPath.copy().addSegment(function.getQualifiedName(), function.getParameters()), - function.getReturnType()); - } - - @Override - @Nonnull - public SingleValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.SingleToSingle action ) - { - final ODataResourcePath actionPath = entityPath.copy().addSegment(action.getQualifiedName()); - final SingleValueActionRequestBuilder requestBuilder = - new SingleValueActionRequestBuilder<>( - servicePath, - actionPath, - action.getParameters(), - action.getReturnType()); - maybeEntity - .filter(e -> e.getVersionIdentifier().isDefined()) - .map(VdmEntity::getVersionIdentifier) - .filter(Option::isDefined) - .map(Option::get) - .forEach(eTag -> requestBuilder.withHeader(HttpHeaders.IF_MATCH, eTag)); - return requestBuilder; - } - - @Override - @Nonnull - public CollectionValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.SingleToCollection action ) - { - final ODataResourcePath actionPath = entityPath.copy().addSegment(action.getQualifiedName()); - final CollectionValueActionRequestBuilder requestBuilder = - new CollectionValueActionRequestBuilder<>( - servicePath, - actionPath, - action.getParameters(), - action.getReturnType()); - maybeEntity - .filter(e -> e.getVersionIdentifier().isDefined()) - .map(VdmEntity::getVersionIdentifier) - .filter(Option::isDefined) - .map(Option::get) - .forEach(eTag -> requestBuilder.withHeader(HttpHeaders.IF_MATCH, eTag)); - return requestBuilder; - } - } - - @RequiredArgsConstructor - static class EntityCollection> - implements - NavigableEntityCollection - { - @Getter - private final String servicePath; - private final ODataResourcePath entityPath; - private final Class navigationType; - - @Nonnull - @Override - public > NavigableEntitySingle forEntity( - @Nonnull final EntityT entity ) - { - return new EntitySingle<>( - servicePath, - entityPath.addParameterToLastSegment(entity.getKey()), - Option.of(entity), - entity.getType()); - } - - @Override - @Nonnull - public GetAllRequestBuilder getAll() - { - return new GetAllRequestBuilder<>(servicePath, entityPath, navigationType); - } - - @Override - @Nonnull - public CreateRequestBuilder create( @Nonnull final NavigationT item ) - { - return new CreateRequestBuilder<>(servicePath, entityPath, item); - } - - @Override - @Nonnull - public CountRequestBuilder count() - { - return new CountRequestBuilder<>(servicePath, entityPath, navigationType); - } - - @Nonnull - @Override - public < - EntityT extends VdmEntity, ResultT extends VdmEntity> - NavigableEntitySingle - withFunction( @Nonnull final BoundFunction.CollectionToSingleEntity.Composable function ) - { - return new ServiceWithNavigableEntitiesImpl.EntitySingle<>( - servicePath, - entityPath.copy().addSegment(function.getQualifiedName(), function.getParameters()), - Option.none(), - function.getReturnType()); - } - - @Nonnull - @Override - public < - EntityT extends VdmEntity, ResultT extends VdmEntity> - NavigableEntityCollection - withFunction( - @Nonnull final BoundFunction.CollectionToCollectionEntity.Composable function ) - { - return new ServiceWithNavigableEntitiesImpl.EntityCollection<>( - servicePath, - entityPath.copy().addSegment(function.getQualifiedName(), function.getParameters()), - function.getReturnType()); - } - - @Nonnull - @Override - public , ResultT> CollectionValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.CollectionToCollection action ) - { - return new CollectionValueActionRequestBuilder<>( - servicePath, - entityPath.copy().addSegment(action.getQualifiedName()), - action.getParameters(), - action.getReturnType()); - } - - @Nonnull - @Override - public , ResultT> SingleValueActionRequestBuilder applyAction( - @Nonnull final BoundAction.CollectionToSingle action ) - { - return new SingleValueActionRequestBuilder<>( - servicePath, - entityPath.copy().addSegment(action.getQualifiedName()), - action.getParameters(), - action.getReturnType()); - } - - @Nonnull - @Override - public , ResultT> SingleValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.CollectionToSingle function ) - { - return newSingleValueFunctionRequestBuilder(servicePath, entityPath, function); - } - - @Nonnull - @Override - public < - EntityT extends VdmEntity, ResultT> CollectionValueFunctionRequestBuilder applyFunction( - @Nonnull final BoundFunction.CollectionToCollection function ) - { - return newCollectionValueFunctionRequestBuilder(servicePath, entityPath, function); - } - } - - private static < - EntityT extends VdmEntity, ResultT> - SingleValueFunctionRequestBuilder - newSingleValueFunctionRequestBuilder( - final String servicePath, - final ODataResourcePath entityPath, - final BoundFunction function ) - { - return new SingleValueFunctionRequestBuilder<>( - servicePath, - entityPath.copy().addSegment(function.getQualifiedName(), function.getParameters()), - function.getReturnType()); - } - - private static < - EntityT extends VdmEntity, ResultT> - CollectionValueFunctionRequestBuilder - newCollectionValueFunctionRequestBuilder( - final String servicePath, - final ODataResourcePath entityPath, - final BoundFunction function ) - { - return new CollectionValueFunctionRequestBuilder<>( - servicePath, - entityPath.copy().addSegment(function.getQualifiedName(), function.getParameters()), - function.getReturnType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SimpleProperty.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SimpleProperty.java deleted file mode 100644 index 15bcc01ee6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SimpleProperty.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Collections; -import java.util.List; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odatav4.expression.FieldOrdering; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableBoolean; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableCollection; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableDate; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableDateTime; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableDuration; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableEnum; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableGuid; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableNumericDecimal; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableNumericInteger; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableString; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableTime; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * Simple property. - * - * @param - */ -@SuppressWarnings( "PMD.UnnecessaryFullyQualifiedName" ) // due to java.lang.String -public interface SimpleProperty extends Property -{ - /** - * A pseudo property referencing all fields. - * - * @param - * The entity type. - */ - class All implements SimpleProperty - { - @Getter - private final java.lang.String fieldName = ""; - @Getter - private final List selections = Collections.singletonList("*"); - } - - /** - * A String property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class String implements SimpleProperty, FilterableString - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * A Boolean property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class Boolean implements SimpleProperty, FilterableBoolean - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * A Decimal property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class NumericDecimal implements SimpleProperty, FilterableNumericDecimal - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * An Integer property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class NumericInteger implements SimpleProperty, FilterableNumericInteger - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * A Guid property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class Guid implements SimpleProperty, FilterableGuid - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * A Binary property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class Binary implements SimpleProperty - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * A Duration property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class Duration implements SimpleProperty, FilterableDuration - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * A DateTime property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class DateTime implements SimpleProperty, FilterableDateTime - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * A Date property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class Date implements SimpleProperty, FilterableDate - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * A Time property. - * - * @param - * The entity type. - */ - @Getter - @RequiredArgsConstructor - class Time implements SimpleProperty, FilterableTime - { - private final Class entityType; - private final java.lang.String fieldName; - } - - /** - * A composite property holding a collection of values. - * - * @param - * The entity type. - * @param - * The collection item type. - */ - @Getter - @RequiredArgsConstructor - class Collection implements SimpleProperty, FilterableCollection - { - private final Class entityType; - private final java.lang.String fieldName; - private final Class itemType; - } - - /** - * A property with predefined possible values. - * - * @param - * The entity type. - * @param - * The Enum type. - */ - @Getter - @RequiredArgsConstructor - class Enum implements SimpleProperty, FilterableEnum - { - private final Class entityType; - private final java.lang.String fieldName; - private final java.lang.String enumType; - } - - /** - * A property for order ascending. - * - * @return The FieldOrdering which has the field and ordering. - */ - @Nonnull - default FieldOrdering asc() - { - return FieldOrdering.asc(this); - } - - /** - * A property for order descending. - * - * @return The FieldOrdering which has the field and ordering. - */ - @Nonnull - default FieldOrdering desc() - { - return FieldOrdering.desc(this); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueActionRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueActionRequestBuilder.java deleted file mode 100644 index a3f81f4846..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueActionRequestBuilder.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import lombok.AccessLevel; -import lombok.Getter; - -/** - * Representation of an OData action request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. This one handles actions where the return type is a single primitive or - * entity value or complex type. It also handles actions with no return type. - * - * @param - * The type of the result entity or complex type or primitive type, if any. For actions that return a single - * value or nothing, the result is wrapped inside a {@link ActionResponseSingle } instance. - */ -public class SingleValueActionRequestBuilder - extends - ActionRequestBuilder, ActionResponseSingle> -{ - @Getter( AccessLevel.PROTECTED ) - private final Class resultClass; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param actionName - * The name of the unbound action - * @param resultClass - * The expected return type of the action. - */ - public SingleValueActionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final String actionName, - @Nonnull final Class resultClass ) - { - super(servicePath, actionName); - this.resultClass = resultClass; - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param actionName - * The name of the unbound action - * @param parameters - * The parameters passed to the action. - * @param resultClass - * The expected return type of the action. - */ - public SingleValueActionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final String actionName, - @Nonnull final Map parameters, - @Nonnull final Class resultClass ) - { - super(servicePath, actionName, parameters); - this.resultClass = resultClass; - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param actionPath - * The path to the unbound action - * @param parameters - * The parameters passed to the action. - * @param resultClass - * The expected return type of the action. - */ - public SingleValueActionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath actionPath, - @Nonnull final Map parameters, - @Nonnull final Class resultClass ) - { - super(servicePath, actionPath, parameters); - this.resultClass = resultClass; - } - - @Nonnull - @Override - public ActionResponseSingle execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - - return ActionResponseSingle.of(response, resultClass); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueFunctionRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueFunctionRequestBuilder.java deleted file mode 100644 index fa1d9d484d..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueFunctionRequestBuilder.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Collections; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataFunctionParameters; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; - -import lombok.AccessLevel; -import lombok.Getter; - -/** - * Representation of an OData function request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. This one handles functions where the return type is a single primitive or - * entity value. - * - * @param - * The type of the result entity, if any. - */ -public class SingleValueFunctionRequestBuilder - extends - FunctionRequestBuilder, ResultT> -{ - @Nonnull - @Getter( AccessLevel.PROTECTED ) - private final Class resultClass; - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param functionName - * The name of the unbound function - * @param resultClass - * The expected return type of the function. - */ - public SingleValueFunctionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final String functionName, - @Nonnull final Class resultClass ) - { - this(servicePath, functionName, Collections.emptyMap(), resultClass); - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param functionName - * The name of the unbound function - * @param parameters - * The parameters passed to the function. - * @param resultClass - * The expected collection return type of the function. - */ - public SingleValueFunctionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final String functionName, - @Nonnull final Map parameters, - @Nonnull final Class resultClass ) - { - this( - servicePath, - ODataResourcePath.of(functionName, ODataFunctionParameters.of(parameters, ODataProtocol.V4)), - resultClass); - } - - /** - * Instantiates this request builder using the given service path to send the requests. - * - * @param servicePath - * The service path to direct the requests to. - * @param functionPath - * The {@link ODataResourcePath} identifying the function to invoke. - * @param resultClass - * The expected collection return type of the function. - */ - SingleValueFunctionRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath functionPath, - @Nonnull final Class resultClass ) - { - super(servicePath, functionPath); - this.resultClass = resultClass; - } - - @Nonnull - @Override - public ResultT execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - final ResultT result = response.as(getResultClass()); - - if( result instanceof VdmEntity ) { - response.getVersionIdentifierFromHeader().peek(((VdmEntity) result)::setVersionIdentifier); - } - return result; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/StructuredProperty.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/StructuredProperty.java deleted file mode 100644 index 74b55e45de..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/StructuredProperty.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -/** - * Interface representing a structural property of {@link EntityT} that points towards an object of {@link TargetT}. - * Structured properties are either complex types or navigational properties. - * - * @see com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty - * @see com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty - * - * @param - * Entity this property is part of. - * @param - * {@link VdmObject} this property represents. - */ -interface StructuredProperty, TargetT extends VdmObject> extends Property -{ -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestBuilder.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestBuilder.java deleted file mode 100644 index 1a688daa8a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestBuilder.java +++ /dev/null @@ -1,295 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; - -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataSerializationException; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ETagSubmissionStrategy; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestUpdate; -import com.sap.cloud.sdk.datamodel.odata.client.request.UpdateStrategy; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -/** - * Representation of an OData request as a fluent interface for further configuring the request and - * {@link #execute(Destination) executing} it. - * - * @param - * The type of the result entity. - */ -@Slf4j -public class UpdateRequestBuilder> - extends - AbstractEntityBasedRequestBuilder, EntityT, ModificationResponse> - implements - ModificationRequestBuilder> -{ - private final Collection includedFields = new HashSet<>(); - - private final Collection excludedFields = new HashSet<>(); - - private UpdateStrategy updateStrategy = UpdateStrategy.MODIFY_WITH_PATCH; - - private ETagSubmissionStrategy eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_ETAG_FROM_ENTITY; - - /** - * The entity object to be updated by calling the {@link #execute(Destination)} method. - */ - @Getter( AccessLevel.PROTECTED ) - @Nonnull - private final EntityT entity; - - /** - * Instantiate an {@code UpdateRequestBuilder}. - * - * @param servicePath - * The service path. - * @param entity - * The entity to update. - * @param entityCollection - * The entity collection - */ - public UpdateRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final EntityT entity, - @Nonnull final String entityCollection ) - { - this(servicePath, ODataResourcePath.of(entityCollection, entity.getKey()), entity); - } - - /** - * Instantiate an {@code UpdateRequestBuilder}. - * - * @param servicePath - * The service path. - * @param entityPath - * The {@link ODataResourcePath} that identifies the entity to update. - * @param entity - * The entity to update. - */ - UpdateRequestBuilder( - @Nonnull final String servicePath, - @Nonnull final ODataResourcePath entityPath, - @Nonnull final EntityT entity ) - { - super(servicePath, entityPath); - this.entity = entity; - } - - @SuppressWarnings( "unchecked" ) - @Nonnull - @Override - protected Class getEntityClass() - { - return (Class) getEntity().getClass(); - } - - /** - * Execute the OData update request for the provided entity. - * - * {@inheritDoc} - * - * @return The wrapped service response, exposing response headers, status code and entity references. If the HTTP - * response is not within healthy bounds, then one of the declared runtime exceptions will be thrown with - * further details. - */ - @Nonnull - @Override - public ModificationResponse execute( @Nonnull final Destination destination ) - { - final HttpClient httpClient = getHttpClient(destination); - - final ODataRequestResultGeneric response = toRequest().execute(httpClient); - - return ModificationResponse.of(response, getEntity()); - } - - /** - * Creates an instance of the {@link ODataRequestUpdate} based on the Entity class. - *

- * The following settings are used to build the Request Builder: - *

    - *
  • the endpoint URL
  • - *
  • the entity collection name
  • - *
  • the key fields of the entity
  • - *
  • the entity JSON payload
  • - *
  • the update strategy (full update or delta)
  • - *
- * - * @return An initialized {@code ODataRequestUpdate}. - * @throws ODataSerializationException - * If entity cannot be serialized for HTTP request. - */ - @Override - @Nonnull - public ODataRequestUpdate toRequest() - { - final String versionIdentifier = - eTagSubmissionStrategy.getHeaderFromVersionIdentifier(entity.getVersionIdentifier()); - final String serializedEntity = getSerializedEntity(); - - final ODataRequestUpdate request = - new ODataRequestUpdate( - getServicePath(), - getResourcePath(), - serializedEntity, - updateStrategy, - versionIdentifier, - ODataProtocol.V4); - - return super.toRequest(request); - } - - /** - * Serialize entity to String depending on update strategy. - * - * @return The serialized String representing the entity. - * @throws ODataSerializationException - * If entity cannot be serialized for HTTP request. - */ - @Nonnull - private String getSerializedEntity() - { - final EntityT entity = getEntity(); - try { - switch( updateStrategy ) { - case REPLACE_WITH_PUT: - return new UpdateRequestHelperPut().toJson(entity, excludedFields); - case MODIFY_WITH_PATCH: - return new UpdateRequestHelperPatch().toJson(entity, includedFields); - default: - throw new IllegalStateException("Unexpected update Strategy: " + updateStrategy); - } - } - catch( final Exception e ) { - final String msg = - String - .format( - "Failed to serialize OData Update HTTP request entity for type %s with strategy %s", - getEntityClass().getSimpleName(), - updateStrategy); - - final ODataRequestUpdate request = - new ODataRequestUpdate( - getServicePath(), - getResourcePath(), - "", - updateStrategy, - eTagSubmissionStrategy.getHeaderFromVersionIdentifier(entity.getVersionIdentifier()), - ODataProtocol.V4); - - throw new ODataSerializationException(request, entity, msg, e); - } - } - - /** - * Allows to explicitly specify entity fields that shall be sent in an update request regardless if the values of - * these fields have been changed. This is helpful in case the API requires to send certain fields in any case in an - * update request. - * - * @param fields - * The fields to be included in the update execution. - * @return The same request builder which will include the specified fields in an update request. - */ - @Nonnull - public final UpdateRequestBuilder includingFields( @Nonnull final FieldReference... fields ) - { - includedFields.addAll(Arrays.asList(fields)); - return this; - } - - /** - * Allows to explicitly specify entity fields that should not be sent in an update request. This is helpful in case - * some services require no read only fields to be sent for update requests. These fields are only excluded in a PUT - * request, they are not considered in a PATCH request. - * - * @param fields - * The fields to be excluded in the update execution. - * @return The same request builder which will exclude the specified fields in an update request. - */ - @Nonnull - public final UpdateRequestBuilder excludingFields( @Nonnull final FieldReference... fields ) - { - Collections.addAll(excludedFields, fields); - return this; - } - - /** - * Allows to control that the request to update the entity is sent with the HTTP method PUT and its payload contains - * all fields of the entity, regardless which of them have been changed. - * - * @return The same request builder which will replace the entity in the remote system - */ - @Nonnull - public final UpdateRequestBuilder replacingEntity() - { - updateStrategy = UpdateStrategy.REPLACE_WITH_PUT; - return this; - } - - /** - * Allows to control that the request to update the entity is sent with the HTTP method PATCH and its payload - * contains the changed fields only. - * - * @return The same request builder which will modify the entity in the remote system. - */ - @Nonnull - public final UpdateRequestBuilder modifyingEntity() - { - updateStrategy = UpdateStrategy.MODIFY_WITH_PATCH; - return this; - } - - /** - * The update request will ignore any version identifier present on the entity and not send an `If-Match` header. - *

- * Warning: This might lead to a response from the remote system that the `If-Match` header is missing. - *

- * It depends on the implementation of the remote system whether the `If-Match` header is expected. - * - * @return The same request builder that will not send the `If-Match` header in the update request - */ - @Nonnull - public UpdateRequestBuilder disableVersionIdentifier() - { - eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_NO_ETAG; - return this; - } - - /** - * The update request will ignore any version identifier present on the entity and update the entity, regardless of - * any changes on the remote entity. - *

- * Warning: Be careful with this option, as this might overwrite any changes made to the remote - * representation of this object. - * - * @return The same request builder that will ignore the version identifier of the entity to update - */ - @Nonnull - public UpdateRequestBuilder matchAnyVersionIdentifier() - { - eTagSubmissionStrategy = ETagSubmissionStrategy.SUBMIT_ANY_MATCH_ETAG; - return this; - } - - @Override - @Nonnull - public UpdateRequestBuilder withoutCsrfToken() - { - setSkipCsrfTokenRetrieval(); - return this; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPatch.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPatch.java deleted file mode 100644 index 5668db98b6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPatch.java +++ /dev/null @@ -1,211 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.common.collect.Maps; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import com.google.gson.TypeAdapter; -import com.google.gson.TypeAdapterFactory; -import com.google.gson.reflect.TypeToken; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -class UpdateRequestHelperPatch -{ - String toJson( @Nonnull final VdmEntity entity, @Nonnull final Collection includedFields ) - { - return new SerializeHelper(entity, includedFields).toJson(); - } - - @RequiredArgsConstructor - private static class SerializeHelper - { - @Nonnull - private final VdmEntity entity; - - @Nonnull - private final Collection includedFields; - - @Nonnull - private final Gson gson = new GsonBuilder().serializeNulls().create(); - - @Nonnull - private final Map, JsonObject> cache = new IdentityHashMap<>(); - - @Nonnull - private static final TypeAdapterFactory GSON_VDM_ADAPTER_FACTORY = new GsonVdmAdapterFactory(); - - @Nonnull - String toJson() - { - // find field names to be patched - final Set fieldNamesToPatch = new HashSet<>(entity.getChangedFields().keySet()); - includedFields.stream().map(FieldReference::getFieldName).forEach(fieldNamesToPatch::add); - log.debug("The following fields are marked for updates: {}.", fieldNamesToPatch); - - // create the key-value map for entity properties to be patched - final Map patchValues = Maps.filterKeys(entity.toMap(), fieldNamesToPatch::contains); - - // starting helper collection to identify of recursion - final Set> parentObjects = Collections.newSetFromMap(new IdentityHashMap<>()); - parentObjects.add(entity); - - // serialize key-value properties map to GSON object. - final JsonObject o = Objects.requireNonNull((JsonObject) serializeComplexValue(patchValues, parentObjects)); - - if( o.size() < 1 ) { - log - .warn( - """ - Update strategy is to modify with PATCH, but no fields have changed. \ - Make sure to modify the entity via its setters or by naming them explicitly via 'includingFields(fields ...)'. \ - This request may be bound to fail in the target system.\ - """); - } - - // add default OData annotation properties, e.g. @odata.type - entity.getAnnotationProperties().forEach(o::addProperty); - - // translate JsonObject to Json String - return gson.toJson(o); - } - - @Nullable - private - JsonElement - serializeComplexValue( @Nullable final Object input, @Nonnull final Set> parentObjects ) - { - if( input == null ) { - return JsonNull.INSTANCE; - } - - if( input instanceof Map ) { - return ((Map) input) - .entrySet() - .stream() - .collect( - JsonObject::new, - ( m, v ) -> m.add(v.getKey().toString(), serializeComplexValue(v.getValue(), parentObjects)), - ( m1, m2 ) -> m2.keySet().forEach(k2 -> m1.add(k2, m2.get(k2)))); - } - if( input instanceof List ) { - return ((Collection) input) - .stream() - .map(v -> serializeComplexValue(v, parentObjects)) - .collect(JsonArray::new, JsonArray::add, JsonArray::addAll); - } - if( input instanceof VdmObject ) { - final JsonObject cachedValue = cache.get(input); - if( cachedValue != null ) { - return cachedValue; - } - - final JsonObject valueMap = new JsonObject(); - if( !parentObjects.contains(input) ) { - cache.put((VdmObject) input, valueMap); - - // for complex type consider all fields, for entity types filter for changed fields - final Map changedFields; - if( input instanceof VdmComplex ) { - changedFields = ((VdmObject) input).toMapOfFields(); - changedFields.putAll(((VdmObject) input).getCustomFields()); - } else { - changedFields = ((VdmObject) input).getChangedFields(); - } - - // derive helper collection to identify of recursion - final Set> nestedParentObjects = Collections.newSetFromMap(new IdentityHashMap<>()); - nestedParentObjects.addAll(parentObjects); - nestedParentObjects.add((VdmObject) input); - - // serialize changed fields of nested VDM object - final JsonElement serializedMapRaw = serializeComplexValue(changedFields, nestedParentObjects); - final JsonObject serializedMap = Objects.requireNonNull((JsonObject) serializedMapRaw); - serializedMap.keySet().forEach(key -> valueMap.add(key, serializedMap.get(key))); - } - - // if nested VDM object is a VDM entity, handle the key properties - if( input instanceof VdmEntity ) { - final VdmEntity entity = (VdmEntity) input; - - // if no key fields are marked as changed, then add the @id property - if( entity.getKey().getFieldNames().stream().noneMatch(valueMap::has) ) { - valueMap.addProperty("@id", entity.getEntityCollection() + entity.getKey().toEncodedString()); - } - } - - return valueMap; - } - - return serializeSimpleValue(input); - } - - @Nullable - private JsonElement serializeSimpleValue( @Nonnull final T object ) - { - @SuppressWarnings( "unchecked" ) - final TypeToken typeToken = TypeToken.get((Class) object.getClass()); - - final TypeAdapter typeAdapter = GSON_VDM_ADAPTER_FACTORY.create(gson, typeToken); - if( typeAdapter != null ) { - final JsonElement jsonObject = typeAdapter.toJsonTree(object); - log.trace("Simple entity property value {} is serialized to {}.", object, jsonObject); - return jsonObject; - } - - log.debug("GSON type adapter could not be found for entity property value of type {}.", typeToken); - - final JsonPrimitive jsonPrimitive = convertToJsonPrimitive(object, typeToken); - - if( jsonPrimitive == null ) { - log - .warn( - "Could not convert value of type {} to a {} representation.", - typeToken, - JsonElement.class.getSimpleName()); - } - - return jsonPrimitive; - } - - private JsonPrimitive convertToJsonPrimitive( final T value, final TypeToken typeToken ) - { - if( Number.class.isAssignableFrom(typeToken.getRawType()) ) { - final Number numberPrimitive = (Number) value; - return new JsonPrimitive(numberPrimitive); - } - - if( Boolean.class.isAssignableFrom(typeToken.getRawType()) ) { - final Boolean booleanPrimitive = (Boolean) value; - return new JsonPrimitive(booleanPrimitive); - } - - if( Character.class.isAssignableFrom(typeToken.getRawType()) ) { - final Character characterPrimitive = (Character) value; - return new JsonPrimitive(characterPrimitive); - } - - return null; - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPut.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPut.java deleted file mode 100644 index 2ceef1b1d5..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPut.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Collection; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; - -class UpdateRequestHelperPut -{ - String toJson( @Nonnull final VdmEntity entity, @Nullable final Collection excludedFields ) - { - final Gson gson = new GsonBuilder().create(); - final JsonObject jsonObject = gson.toJsonTree(entity).getAsJsonObject(); - - // find field names to be removed from PUT request - if( excludedFields != null ) { - excludedFields.stream().map(FieldReference::getFieldName).forEach(jsonObject::remove); - } - - return gson.toJson(jsonObject); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmComplex.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmComplex.java deleted file mode 100644 index ffd7382fe3..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmComplex.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -/** - * Complex type in the virtual data model. - * - * @param - * Object type of the complex type. - */ -public abstract class VdmComplex extends VdmObject -{ - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntity.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntity.java deleted file mode 100644 index 5b35788bb2..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntity.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import io.vavr.control.Option; -import lombok.EqualsAndHashCode; - -/** - * Base class for an OData entity. - * - * @param - * The entity type. - */ -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -public abstract class VdmEntity extends VdmObject -{ - @Nullable - private String versionIdentifier = null; - - /** - * Select all properties of an entity. - * - * @param - * The entity type. - * @return A selector for all entity fields. - */ - @Nonnull - protected static SimpleProperty all() - { - return new SimpleProperty.All<>(); - } - - /** - * - * Getter for the version identifier of this entity. - *

- * This identifier can be used to compare this entity with a remote one. As not the whole entity has to be sent this - * reduces the request overhead. - *

- * Actual use cases can be checking whether this entity is still current with regards to the remote entity, and - * ensuring that a update/delete operation is done on the expected version of the remote entity. - * - * @return The version identifier. - */ - @Nonnull - public Option getVersionIdentifier() - { - return Option.of(versionIdentifier); - } - - /** - * Setter for the version identifier of this entity. - *

- * This identifier can be used to compare this entity with a remote one. As not the whole entity has to be sent this - * reduces the request overhead. - *

- * Actual use cases can be checking whether this entity is still current with regards to the remote entity, and - * ensuring that a update/delete operation is done on the expected version of the remote entity. - * - * @param versionIdentifier - * The version identifier of this entity. - */ - public void setVersionIdentifier( @Nullable final String versionIdentifier ) - { - this.versionIdentifier = versionIdentifier; - } - - /** - * Used by request builders and navigation property methods to construct OData requests. - * - * @return EDMX name of the entity collection identifier. - */ - @Nonnull - protected abstract String getEntityCollection(); - - /** - * Used by request builders and navigation property methods to construct OData requests. - * - * @return Default context path to the OData service. In other words, everything in between the - * {@code protocol://hostname:port} and the OData resource name (entity set, {@code $metadata}, etc.) - */ - @Nullable - protected String getDefaultServicePath() - { - return null; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntitySet.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntitySet.java deleted file mode 100644 index c3115b150a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntitySet.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -/** - * Empty interface to indicate operative support for querying the OData service for the related EntitySet. - */ -public interface VdmEntitySet -{ -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntityUtil.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntityUtil.java deleted file mode 100644 index 4b1f14deb2..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntityUtil.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.lang.reflect.InvocationTargetException; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.cloudplatform.exception.ShouldNotHappenException; - -import lombok.RequiredArgsConstructor; - -/** - * Utility class to manage OData entity deserialization. - * - * @param - * Entity type to create new instances from. - */ -@RequiredArgsConstructor -final class VdmEntityUtil> -{ - private final Class entityClass; - - @Nonnull - EntityT newInstance() - { - try { - return entityClass.getDeclaredConstructor().newInstance(); - } - catch( final - NoSuchMethodException - | InvocationTargetException - | InstantiationException - | IllegalAccessException e ) { - throw new ShouldNotHappenException( - "Failed to instantiate object of type " + entityClass.getSimpleName(), - e); - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEnum.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEnum.java deleted file mode 100644 index 6775a4bf29..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEnum.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Arrays; -import java.util.Objects; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -/** - * Interface to manage the different properties an Edm.Enum literal contains. - */ -public interface VdmEnum -{ - /** - * Get the name property of the Edm.Enum literal - * - * @return The name property. - */ - @Nonnull - default String getName() - { - return toString(); - } - - /** - * Get the value property of the Edm.Enum literal - * - * @return The value property. - */ - @Nullable - default Long getValue() - { - return null; - } - - /** - * Helper function to resolve enum constant from given type reference and String identifier. - * - * @param enumType - * The enum type reference. - * @param identifier - * The enum constant identifier. - * @param - * The generic enum type. - * @return A - */ - @Nullable - static T getConstant( @Nonnull final Class enumType, @Nullable final String identifier ) - { - final T[] enumConstants = enumType.getEnumConstants(); - if( enumConstants == null ) { - return null; - } - return Arrays - .stream(enumConstants) - .filter(member -> Objects.equals(member.getName(), identifier)) - .findFirst() - .orElse(null); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmObject.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmObject.java deleted file mode 100644 index 7dedb8f7f8..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmObject.java +++ /dev/null @@ -1,403 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Set; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.collect.Streams; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; - -import lombok.EqualsAndHashCode; -import lombok.ToString; -import lombok.extern.slf4j.Slf4j; - -/** - * Superclass of all entities which contains common elements such as a generic representation of custom fields. - * - * @param - * The type of the implementing object. - */ -@Slf4j -@ToString( doNotUseGetters = true ) -@EqualsAndHashCode( doNotUseGetters = true ) -@JsonAutoDetect( - fieldVisibility = JsonAutoDetect.Visibility.ANY, - getterVisibility = JsonAutoDetect.Visibility.NONE, - isGetterVisibility = JsonAutoDetect.Visibility.NONE, - setterVisibility = JsonAutoDetect.Visibility.NONE, - creatorVisibility = JsonAutoDetect.Visibility.NONE ) -public abstract class VdmObject -{ - /** - * The OData V4 JSON key to access an entity EDM type definition. - */ - public static final String[] ODATA_TYPE_ANNOTATIONS = { "@odata.type", "@type" }; - - /** - * The OData V4 JSON key to access an entity version identifier. - */ - public static final String[] ODATA_VERSION_ANNOTATIONS = { "@odata.etag", "@etag" }; - - @JsonIgnore - @Nonnull - private final transient Map customFields = new LinkedHashMap<>(); - - /** - * A mapping of the OData field name to the original value. - *

- * This should be updated via {@link #rememberChangedField(String, Object)} on every set call of a property. - */ - @JsonIgnore - @Nonnull - protected final transient Map changedOriginalFields = new HashMap<>(); - - /** - * Returns the names of the custom fields of this object. - * - * @return The names of the custom fields of this object. - */ - @Nonnull - public Set getCustomFieldNames() - { - return customFields.keySet(); - } - - /** - * Returns the names and values of a custom field. - * - * @return All the names & values of custom fields as a map. - */ - @JsonAnyGetter - @Nonnull - public Map getCustomFields() - { - return customFields; - } - - /** - * Sets the value of a single custom field. - * - * @param customFieldName - * Name of the custom field. - * @param value - * Value of the custom field. - */ - @JsonAnySetter - public void setCustomField( @Nonnull final String customFieldName, @Nullable final Object value ) - { - rememberChangedField(customFieldName, customFields.get(customFieldName)); - customFields.put(customFieldName, value); - } - - /** - * Sets the value of a single custom field. If the EntityField passed as parameter holds a TypeConverter, the value - * will be converted before it's stored. - * - * @param customField - * Name of the custom field, represented as an EntityField object. - * @param value - * Value of the custom field. - * @param - * The type of the custom field to set. - */ - public < - FieldT> void setCustomField( @Nonnull final SimpleProperty customField, @Nullable final FieldT value ) - { - setCustomField(customField.getFieldName(), value); - } - - /** - * Checks whether this object contains a custom field with the given name. - * - * @param customFieldName - * Name of the custom field to check for - * - * @return {@code true} if this entity has a custom field with the given name, {@code false} otherwise. - */ - public boolean hasCustomField( @Nonnull final String customFieldName ) - { - return customFields.containsKey(customFieldName); - } - - /** - * Checks whether this object contains a value for the given custom field. - * - * @param customField - * Custom field to check for, represented as an {@code EntityField} object. - * - * @return {@code true} if this object has a custom field with the name of the given field, {@code false} otherwise. - */ - public boolean hasCustomField( @Nonnull final SimpleProperty customField ) - { - return hasCustomField(customField.getFieldName()); - } - - /** - * This method allows for retrieval of custom fields that are added to the underlying OData services. - * - * @param customFieldName - * Name of the field returned by the underlying OData service. - * @param - * The type of the returned field. - * - * @return The value of the custom field. Actual type will depend on the type configured in the underlying OData - * service. - * - * @throws NoSuchElementException - * if no field with the given name could be found. - */ - @SuppressWarnings( "unchecked" ) - @Nullable - public FieldT getCustomField( @Nonnull final String customFieldName ) - throws NoSuchElementException - { - if( !hasCustomField(customFieldName) ) { - final String msg = "Object has no field with name '" + customFieldName + "'."; - log.debug(msg); - throw new NoSuchElementException(msg); - } - return (FieldT) customFields.get(customFieldName); - } - - /** - * This method allows for retrieval of custom fields that are added to the underlying OData services. If the - * EntityField passed as parameter holds a TypeConverter, the value will be converted before it's returned. - * - * @param customField - * Field returned by the underlying OData service. - * @param - * The type of the returned field. - * - * @return The value of the custom field. Actual type will depend on the type configured in the underlying OData - * service. - * - * @throws NoSuchElementException - * if no field with the given name could be found. - */ - @Nullable - public FieldT getCustomField( @Nonnull final SimpleProperty customField ) - throws NoSuchElementException - { - return getCustomField(customField.getFieldName()); - } - - /** - * Returns the annotation properties. - * - * @return List of OData annotation properties. - */ - @Nonnull - public Map getAnnotationProperties() - { - final Map properties = new HashMap<>(); - properties.put(ODATA_TYPE_ANNOTATIONS[0], "#" + getOdataType()); - - return properties; - } - - /** - * Returns the EDMX type of this entity. - * - * @return The EDMX type of this entity. - */ - @Nonnull - public abstract String getOdataType(); - - /** - * Returns the class of this object. - * - * @return The class of this object. - */ - @Nonnull - public abstract Class getType(); - - /** - * Returns the compound key of this object. - * - * @return The compound key of this object. - */ - @Nonnull - protected ODataEntityKey getKey() - { - return new ODataEntityKey(ODataProtocol.V4); - } - - /** - * Read entity data from generic map. - * - * @param values - * The key-value map. - */ - protected void fromMap( final Map values ) - { - for( final Map.Entry entry : values.entrySet() ) { - setCustomField(entry.getKey(), entry.getValue()); - } - - resetChangedFields(); - } - - /** - * Get the custom fields as value map. - * - * @return The custom fields. - */ - @Nonnull - protected Map toMapOfCustomFields() - { - return new HashMap<>(getCustomFields()); - } - - /** - * Get the custom field names. - * - * @return The custom field names. - */ - @Nonnull - protected Set getSetOfCustomFields() - { - return new HashSet<>(getCustomFields().keySet()); - } - - /** - * Get all fields as map. - * - * @return The fields as map. - */ - @Nonnull - protected Map toMapOfFields() - { - return new HashMap<>(); - } - - /** - * Get the field names. - * - * @return The field names. - */ - @Nonnull - protected Set getSetOfFields() - { - return Sets.newHashSet(toMapOfFields().keySet()); - } - - /** - * Get navigation properties as map. - * - * @return The navigation properties. - */ - @Nonnull - protected Map toMapOfNavigationProperties() - { - return new HashMap<>(); - } - - /** - * Get navigation property names as set. - * - * @return The navigation property names. - */ - @Nonnull - protected Set getSetOfNavigationProperties() - { - return Sets.newHashSet(toMapOfNavigationProperties().keySet()); - } - - /** - * Translate the entity data to key-value map. - * - * @return The map representation of the entity. - */ - @Nonnull - protected Map toMap() - { - final Map values = new HashMap<>(); - - values.putAll(toMapOfFields()); - values.putAll(toMapOfNavigationProperties()); - values.putAll(toMapOfCustomFields()); - - return values; - } - - /** - * Returns map of all fields which have been changed on this entity along with their updated values. - * - * @return Map containing all changed fields with their current value. - */ - @Nonnull - public Map getChangedFields() - { - final Map currentFields = new HashMap<>(); - currentFields.putAll(toMapOfFields()); - currentFields.putAll(getCustomFields()); - - return Maps.filterEntries(currentFields, f -> f != null && isFieldChanged(f.getKey(), f.getValue())); - } - - private boolean isFieldChanged( @Nonnull final String fieldName, @Nullable final Object currentValue ) - { - final Object overriddenValue = changedOriginalFields.get(fieldName); - if( currentValue == null && overriddenValue == null ) { - return false; - } - - if( changedOriginalFields.containsKey(fieldName) && !Objects.equals(currentValue, overriddenValue) ) { - return true; - } - - // property was either not updated directly, or the values are still "equal" (according to Objects.equals) - if( currentValue instanceof VdmObject ) { - return !((VdmObject) currentValue).getChangedFields().isEmpty(); - } - - if( currentValue instanceof Iterable ) { - return Streams - .stream((Iterable) currentValue) - .filter(VdmObject.class::isInstance) - .anyMatch(obj -> !((VdmObject) obj).getChangedFields().isEmpty()); - } - - return false; - } - - /** - * Remembers the original value of a changed field. - * - * @param fieldName - * The name of the field that is changed. - * @param valueBeforeChange - * The original value before the change. - */ - protected void rememberChangedField( @Nonnull final String fieldName, @Nullable final Object valueBeforeChange ) - { - if( !changedOriginalFields.containsKey(fieldName) ) { - changedOriginalFields.put(fieldName, valueBeforeChange); - } - } - - /** - * Resets the map of all fields which have been changed on this entity. - *

- * After calling this method, no field is considered changed, until you change the value of fields on this entity - * afterwards. - */ - public void resetChangedFields() - { - changedOriginalFields.clear(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/EntityReference.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/EntityReference.java deleted file mode 100644 index 0eb3d6531e..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/EntityReference.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import javax.annotation.Nonnull; - -/** - * Generic interface to provide the original entity class reference. - * - * @param - * Type of the entity which references the value. - */ -public interface EntityReference -{ - /** - * Get the type of the entity which references the value. - * - * @return The entity type, - */ - @Nonnull - Class getEntityType(); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FieldOrdering.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FieldOrdering.java deleted file mode 100644 index 18af8e7602..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FieldOrdering.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.Queue; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.OrderExpression; -import com.sap.cloud.sdk.datamodel.odata.client.query.Order; -import com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -/** - * Represents an asc/desc ordering of an entity via a property. - * - * @param - * The entity type that is ordered. - */ -@Getter -@RequiredArgsConstructor -public class FieldOrdering -{ - private final SimpleProperty property; - private final Order order; - - /** - * Creates a {@code FieldOrdering} representing an ascending order over the given property - * - * @param - * The entity the ordering is applied to. - * @param property - * The property of {@code EntityT} to be sorted in descending order. - * @return The FieldOrdering. - */ - @Nonnull - public static FieldOrdering asc( @Nonnull final SimpleProperty property ) - { - return new FieldOrdering<>(property, Order.ASC); - } - - /** - * Creates a {@code FieldOrdering} representing an descending order over the given property. - * - * @param - * The entity the ordering is applied to. - * @param property - * The property of {@code EntityT} to be sorted in descending order. - * @return The FieldOrdering. - */ - @Nonnull - public static FieldOrdering desc( @Nonnull final SimpleProperty property ) - { - return new FieldOrdering<>(property, Order.DESC); - } - - /** - * Builds an {@link OrderExpression} out of individual field orderings. The expression represents a sorting where - * the orderings are applied in the order they are given. - * - * @param orderings - * The {@code FieldOrdering}s that should be applied to achieve a sorting. - * @return The resulting {@link OrderExpression} or {@code null}, if no orderings where given. - */ - @Nullable - public static OrderExpression toOrderExpression( @Nonnull final FieldOrdering... orderings ) - { - final Queue> fieldOrderings = new LinkedList<>(Arrays.asList(orderings)); - if( fieldOrderings.isEmpty() ) { - return null; - } - final FieldOrdering first = fieldOrderings.poll(); - final OrderExpression expr = OrderExpression.of(first.getProperty().getFieldName(), first.getOrder()); - fieldOrderings.forEach(o -> expr.and(o.getProperty().getFieldName(), o.getOrder())); - return expr; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableBoolean.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableBoolean.java deleted file mode 100644 index f237367500..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableBoolean.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.Expressions; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionLogical; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Boolean. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableBoolean extends FilterableValue -{ - /** - * Wrapper expression class, which delegates to another operation. - * - * @param - * Type of the entity which references the value. - */ - @RequiredArgsConstructor - @Getter - class Expression implements FilterableBoolean, FilterExpression - { - @Delegate - private final FilterExpression delegate; - - @Nonnull - private final Class entityType; - } - - /*** - * Creates a new Expression from a custom filter expression. Allows for untyped expressions to be supplied to the - * VDM. - * - * @param delegateExpression - * The expression to which the Expression delegates. - * @param entityType - * The expected entity type. - * @param - * Type of the entity which references the value. - * @return A new Expression. - * - * @since 4.8.0 - */ - @Nonnull - static FilterableBoolean fromCustomFilter( - @Nonnull final ValueBoolean delegateExpression, - @Nonnull final Class entityType ) - { - return new Expression<>((FilterExpression) delegateExpression, entityType); - } - - /** - * Combine current filter expression with another expression in conjunction. - * - * @param operand - * The other expression. - * @return This FluentHelper reference. - */ - @Nonnull - default FilterableBoolean and( @Nonnull final FilterableBoolean operand ) - { - final ValueBoolean value = operand::getExpression; - final ValueBoolean.Expression expression = FilterExpressionLogical.and(this::getExpression, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * Combine the filter expression with a boolean value in conjunction. - * - * @param operand - * A boolean value. - * @return This FluentHelper reference. - */ - @Nonnull - default FilterableBoolean and( @Nonnull final Boolean operand ) - { - final ValueBoolean value = (ValueBoolean) Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionLogical.and(this::getExpression, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * Combine the filter expression with another expression in disjunction. - * - * @param operand - * The other expression. - * @return This FluentHelper reference. - */ - @Nonnull - default FilterableBoolean or( @Nonnull final FilterableBoolean operand ) - { - final ValueBoolean value = operand::getExpression; - final ValueBoolean.Expression expression = FilterExpressionLogical.or(this::getExpression, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * Combine the filter expression with a boolean value in disjunction. - * - * @param operand - * The other expression. - * @return This FluentHelper reference. - */ - @Nonnull - default FilterableBoolean or( @Nonnull final Boolean operand ) - { - final ValueBoolean value = (ValueBoolean) Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionLogical.or(this::getExpression, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * Negate the filter expression. - * - * @return This FluentHelper reference. - */ - @Nonnull - default FilterableBoolean not() - { - final ValueBoolean.Expression expression = FilterExpressionLogical.not(this::getExpression); - return new Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableCollection.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableCollection.java deleted file mode 100644 index 37b57b23e7..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableCollection.java +++ /dev/null @@ -1,343 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import java.util.function.Predicate; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.Expressions; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionCollection; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueCollection; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueNumeric; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Collection. - * - * @param - * Type of the entity which references the value. - * @param - * Type of the item type the collection holds. - */ -public interface FilterableCollection extends Expressions.OperandMultiple, EntityReference -{ - /** - * Get the item type the collection holds. - * - * @return The item type. - */ - @Nonnull - Class getItemType(); - - /** - * Wrapper expression class, which delegates to another operation. - * - * @param - * Type of the entity which references the value. - * @param - * Type of the item type the collection holds. - */ - @RequiredArgsConstructor - @Getter - class Expression implements FilterableCollection - { - @Delegate - @Nonnull - private final FilterExpression delegate; - - @Nonnull - private final Class entityType; - - @Nonnull - private final Class itemType; - } - - /** - * Filter by expression "hasSubset". - * - * @param operand - * Only operand of collection type. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean hasSubset( @Nonnull final FilterableCollection operand ) - { - final FilterExpression expression = FilterExpressionCollection.hasSubset(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "hasSubset". - * - * @param operand - * Only operand of Java iterable. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean hasSubset( @Nonnull final Iterable operand ) - { - final ValueCollection value = ValueCollection.literal(operand); - final FilterExpression expression = FilterExpressionCollection.hasSubset(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "hasSubSequence". - * - * @param operand - * Only operand of collection type. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean hasSubSequence( @Nonnull final FilterableCollection operand ) - { - final FilterExpression expression = FilterExpressionCollection.hasSubSequence(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "hasSubSequence". - * - * @param operand - * Only operand of Java iterable. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean hasSubSequence( @Nonnull final Iterable operand ) - { - final ValueCollection value = ValueCollection.literal(operand); - final FilterExpression expression = FilterExpressionCollection.hasSubSequence(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "contains". - * - * @param operand - * Only operand of collection type. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean contains( @Nonnull final FilterableCollection operand ) - { - final FilterExpression expression = FilterExpressionCollection.contains(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "contains". - * - * @param operand - * Only operand of Java iterable. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean contains( @Nonnull final Iterable operand ) - { - final Expressions.OperandMultiple value = ValueCollection.literal(operand); - final FilterExpression expression = FilterExpressionCollection.contains(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "startsWith". - * - * @param operand - * Only operand of collection type. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean startsWith( @Nonnull final FilterableCollection operand ) - { - final FilterExpression expression = FilterExpressionCollection.startsWith(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "startsWith". - * - * @param operand - * Only operand of Java iterable. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean startsWith( @Nonnull final Iterable operand ) - { - final ValueCollection value = ValueCollection.literal(operand); - final FilterExpression expression = FilterExpressionCollection.startsWith(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "endsWith". - * - * @param operand - * Only operand of collection type. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean endsWith( @Nonnull final FilterableCollection operand ) - { - final FilterExpression expression = FilterExpressionCollection.endsWith(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "endsWith". - * - * @param operand - * Only operand of Java iterable. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean endsWith( @Nonnull final Iterable operand ) - { - final ValueCollection value = ValueCollection.literal(operand); - final FilterExpression expression = FilterExpressionCollection.endsWith(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "indexOf". - * - * @param operand - * Only operand of collection type. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger indexOf( @Nonnull final FilterableCollection operand ) - { - final FilterExpression expression = FilterExpressionCollection.indexOf(this, operand); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "indexOf". - * - * @param operand - * Only operand of Java iterable. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger indexOf( @Nonnull final Iterable operand ) - { - final ValueCollection value = ValueCollection.literal(operand); - final FilterExpression expression = FilterExpressionCollection.indexOf(this, value); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "concat". - * - * @param operand - * Only operand of collection type. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableCollection concat( @Nonnull final FilterableCollection operand ) - { - final FilterExpression expression = FilterExpressionCollection.concat(this, operand); - return new Expression<>(expression, getEntityType(), getItemType()); - } - - /** - * Filter by expression "concat". - * - * @param operand - * Only operand of Java iterable. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableCollection concat( @Nonnull final Iterable operand ) - { - final ValueCollection value = ValueCollection.literal(operand); - final FilterExpression expression = FilterExpressionCollection.concat(this, value); - return new Expression<>(expression, getEntityType(), getItemType()); - } - - /** - * Filter by expression "length". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger length() - { - final FilterExpression expression = FilterExpressionCollection.length(this); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "substring". - * - * @param operand - * Only operand of Integer type. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableCollection substring( @Nonnull final Integer operand ) - { - final ValueNumeric value = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionCollection.substring(this, value); - return new Expression<>(expression, getEntityType(), getItemType()); - } - - /** - * Filter by expression "substring". - * - * @param operandIndex - * Operand of Integer type to mark the start of the subset. - * @param operandLength - * Operand of Integer type to mark the size of the subset. - * @return The FluentHelper filter. - */ - @Nonnull - default - FilterableCollection - substring( @Nonnull final Integer operandIndex, @Nonnull final Integer operandLength ) - { - final ValueNumeric value1 = ValueNumeric.literal(operandIndex); - final ValueNumeric value2 = ValueNumeric.literal(operandLength); - final ValueCollection.Expression expression = FilterExpressionCollection.substring(this, value1, value2); - return new Expression<>(expression, getEntityType(), getItemType()); - } - - /** - * Filter by lambda expression "all". - * - * @param operand - * Operand to provide a generic filter to the collection item. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean all( @Nonnull final FilterableBoolean operand ) - { - final Predicate lambdaFieldPredicate = - o -> o instanceof EntityReference && ((EntityReference) o).getEntityType().equals(getItemType()); - final ValueBoolean.Expression expression = - FilterExpressionCollection.all(this, operand::getExpression, lambdaFieldPredicate); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by lambda expression "any". - * - * @param operand - * Operand to provide a generic filter to the collection item. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean any( @Nonnull final FilterableBoolean operand ) - { - final Predicate lambdaFieldPredicate = - o -> o instanceof EntityReference && ((EntityReference) o).getEntityType().equals(getItemType()); - final FilterExpression expression = - FilterExpressionCollection.any(this, operand::getExpression, lambdaFieldPredicate); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableComplex.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableComplex.java deleted file mode 100644 index 427b8fc1f4..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableComplex.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.Expressions; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionLogical; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueEnum; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by an OData complex property. - * - * @param - * Type of the entity which references the value. - * @param - * Type of the complex property. - */ -public interface FilterableComplex extends Expressions.OperandSingle, EntityReference -{ - /** - * Filter by expression "has". - * - * @param operand - * A generic String to be applied to the expression - * @return The FluentHelper filter - */ - @Nonnull - default FilterableBoolean has( @Nonnull final String operand ) - { - final ValueEnum value = ValueEnum.literal(operand); - final FilterExpression expression = FilterExpressionLogical.has(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "has". - * - * @param operand - * A generic String to be applied to the expression - * @param - * The enum value type. - * @return The FluentHelper filter - */ - @Nonnull - default < - EnumT extends VdmEnum> FilterableBoolean has( @Nonnull final FilterableEnum operand ) - { - final FilterExpression expression = FilterExpressionLogical.has(this, operand::getExpression); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "in". - * - * @param operand - * The generic operands to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean in( @Nonnull final FilterableCollection operand ) - { - final ValueBoolean.Expression expression = FilterExpressionLogical.in(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "eq null". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean equalToNull() - { - final ValueBoolean.Expression expression = FilterExpressionLogical.equalTo(this, Expressions.Operand.NULL); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ne null". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean notEqualToNull() - { - final ValueBoolean.Expression expression = FilterExpressionLogical.notEqualTo(this, Expressions.Operand.NULL); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableDate.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableDate.java deleted file mode 100644 index 641cb59644..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableDate.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import java.time.Duration; -import java.time.LocalDate; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionArithmetic; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionTemporal; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueDate; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueDuration; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Date. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableDate extends FilterableValue -{ - /** - * Wrapper expression class, which delegates to another operation. - * - * @param - * Type of the entity which references the value. - */ - @RequiredArgsConstructor - @Getter - class Expression implements FilterableDate, FilterExpression - { - @Delegate - @Nonnull - private final FilterExpression delegate; - - @Nonnull - private final Class entityType; - } - - /** - * - * Filter by expression "day". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger dateDay() - { - final ValueDate thisDate = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.day(thisDate); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "month". - * - * @return The FluentHelper filter - */ - @Nonnull - default FilterableNumericInteger dateMonth() - { - final ValueDate thisDate = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.month(thisDate); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "year". - * - * @return The FluentHelper filter - */ - @Nonnull - default FilterableNumericInteger dateYear() - { - final ValueDate thisDate = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.year(thisDate); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "add". - * - * @param operand - * The duration to add to the date expression. - * @return The FluentHelper filter - */ - @Nonnull - default FilterableDate add( @Nonnull final FilterableDuration operand ) - { - final ValueDate thisDate = this::getExpression; - final ValueDuration value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.add(thisDate, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "add". - * - * @param operand - * The duration to ad to the date expression. - * - * @return The FluentHelper filter - */ - @Nonnull - default FilterableDate add( @Nonnull final Duration operand ) - { - final ValueDate thisDate = this::getExpression; - final ValueDuration value = ValueDuration.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.add(thisDate, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "sub". - * - * @param operand - * The duration to subtract from the date. - * - * @return The FluentHelper filter - */ - @Nonnull - default FilterableDate subtract( @Nonnull final FilterableDuration operand ) - { - final ValueDate thisDate = this::getExpression; - final ValueDuration value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisDate, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "sub". - * - * @param operand - * The duration to subtract from the date. - * - * @return The FluentHelper filter - */ - @Nonnull - default FilterableDate subtract( @Nonnull final Duration operand ) - { - final ValueDate thisDate = this::getExpression; - final ValueDuration value = ValueDuration.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisDate, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "sub". - * - * @param operand - * The other date to calculate the difference from. - * - * @return The FluentHelper filter - */ - @Nonnull - default FilterableDuration difference( @Nonnull final FilterableDate operand ) - { - final ValueDate thisDate = this::getExpression; - final ValueDuration value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisDate, value); - return new FilterableDuration.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "sub". - * - * @param operand - * The other date to calculate the difference from. - * - * @return The FluentHelper filter - */ - @Nonnull - default FilterableDuration difference( @Nonnull final LocalDate operand ) - { - final ValueDate thisDate = this::getExpression; - final ValueDate value = ValueDate.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisDate, value); - return new FilterableDuration.Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableDateTime.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableDateTime.java deleted file mode 100644 index ed6990ba42..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableDateTime.java +++ /dev/null @@ -1,255 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import java.time.Duration; -import java.time.OffsetDateTime; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionArithmetic; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionTemporal; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueDateTimeOffset; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueDuration; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by DateTime. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableDateTime extends FilterableValue -{ - /** - * Wrapper expression class, which delegates to another operation. - * - * @param - * Type of the entity which references the value. - */ - @RequiredArgsConstructor - @Getter - class Expression implements FilterableDateTime, FilterExpression - { - @Delegate - @Nonnull - private final FilterExpression delegate; - - @Nonnull - private final Class entityType; - } - - /** - * - * Filter by expression "date". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDate date() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.date(thisDateTime); - return new FilterableDate.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "fractionalseconds". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericDecimal timeFractionalSeconds() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.fractionalSeconds(thisDateTime); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "second". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger timeSecond() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.second(thisDateTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "minute". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger timeMinute() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.minute(thisDateTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "hour". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger timeHour() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.hour(thisDateTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "day". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger dateDay() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.day(thisDateTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "month". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger dateMonth() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.month(thisDateTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "year". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger dateYear() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.year(thisDateTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "time". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableTime time() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.time(thisDateTime); - return new FilterableTime.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "offsetminutes". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger offsetMinutes() - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.totalOffsetMinutes(thisDateTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "add". - * - * @param operand - * The duration to add to the date time. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDateTime add( @Nonnull final FilterableDuration operand ) - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final ValueDuration value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.add(thisDateTime, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "add". - * - * @param operand - * The duration to add to the date time. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDateTime add( @Nonnull final Duration operand ) - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final ValueDuration value = ValueDuration.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.add(thisDateTime, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "sub". - * - * @param operand - * The duration to subtract from the date time. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDateTime subtract( @Nonnull final FilterableDuration operand ) - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final ValueDuration value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisDateTime, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "add". - * - * @param operand - * The duration to subtract from the date time. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDateTime subtract( @Nonnull final Duration operand ) - { - final ValueDateTimeOffset thisDateTime = this::getExpression; - final ValueDuration value = ValueDuration.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisDateTime, value); - return new Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableDuration.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableDuration.java deleted file mode 100644 index 626d26eef3..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableDuration.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import java.time.Duration; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionArithmetic; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionTemporal; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueDuration; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueNumeric; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Duration. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableDuration extends FilterableValue -{ - /** - * Wrapper expression class, which delegates to another operation. - * - * @param - * Type of the entity which references the value. - */ - @RequiredArgsConstructor - @Getter - class Expression implements FilterableDuration, FilterExpression - { - @Delegate - @Nonnull - private final FilterExpression delegate; - - @Nonnull - private final Class entityType; - } - - /** - * - * Filter by expression "offsetseconds". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger offsetSeconds() - { - final ValueDuration thisDuration = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.totalOffsetSeconds(thisDuration); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "add". - * - * @param operand - * The duration to add to the duration. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDuration add( @Nonnull final FilterableDuration operand ) - { - final ValueDuration thisDuration = this::getExpression; - final ValueDuration value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.add(thisDuration, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "add". - * - * @param operand - * The duration to add to the duration. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDuration add( @Nonnull final Duration operand ) - { - final ValueDuration thisDuration = this::getExpression; - final ValueDuration value = ValueDuration.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.add(thisDuration, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "sub". - * - * @param operand - * The duration to subtract from this duration. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDuration subtract( @Nonnull final FilterableDuration operand ) - { - final ValueDuration thisDuration = this::getExpression; - final ValueDuration value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisDuration, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "sub". - * - * @param operand - * The duration to subtract from this duration. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDuration subtract( @Nonnull final Duration operand ) - { - final ValueDuration thisDuration = this::getExpression; - final ValueDuration value = ValueDuration.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisDuration, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "mul". - * - * @param operand - * The product to be used to multiply the duration. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDuration multiply( @Nonnull final FilterableNumeric operand ) - { - final ValueDuration thisDuration = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisDuration, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "mul". - * - * @param operand - * The product to be used to multiply the duration. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDuration multiply( @Nonnull final Number operand ) - { - final ValueDuration thisDuration = this::getExpression; - final ValueNumeric value = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisDuration, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "div". - * - * @param operand - * The quotient to be used to divide the duration. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDuration divide( @Nonnull final FilterableNumeric operand ) - { - final ValueDuration thisDuration = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.divide(thisDuration, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "div". - * - * @param operand - * The quotient to be used to divide the duration. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDuration divide( @Nonnull final Number operand ) - { - final ValueDuration thisDuration = this::getExpression; - final ValueNumeric value = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.divide(thisDuration, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "-". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableDuration negate() - { - final ValueDuration thisDuration = this::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.negate(thisDuration); - return new Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableEnum.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableEnum.java deleted file mode 100644 index f8d1b274ab..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableEnum.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.Expressions; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionLogical; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueEnum; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Enum. - * - * @param - * Type of the entity which references the value. - * @param - * Type of the Enum value. - */ -public interface FilterableEnum extends Expressions.Operand, EntityReference -{ - /** - * OData Enum type identifier. - * - * @return The enum type identifier. - */ - @Nonnull - String getEnumType(); - - /** - * - * Filter by expression "eq". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean equalTo( @Nonnull final FilterableEnum operand ) - { - final FilterExpression expression = FilterExpressionLogical.equalTo(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "eq". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean equalTo( @Nullable final EnumT operand ) - { - final Expressions.Operand value = - operand == null ? Expressions.Operand.NULL : ValueEnum.literal(getEnumType(), operand.getName()); - final FilterExpression expression = FilterExpressionLogical.equalTo(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ne". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean notEqualTo( @Nonnull final FilterableEnum operand ) - { - final FilterExpression expression = FilterExpressionLogical.notEqualTo(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ne". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean notEqualTo( @Nullable final EnumT operand ) - { - final Expressions.Operand value = - operand == null ? Expressions.Operand.NULL : ValueEnum.literal(getEnumType(), operand.getName()); - final FilterExpression expression = FilterExpressionLogical.notEqualTo(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "eq null". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean equalToNull() - { - final ValueBoolean.Expression expression = FilterExpressionLogical.equalTo(this, Expressions.Operand.NULL); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ne null". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean notEqualToNull() - { - final ValueBoolean.Expression expression = FilterExpressionLogical.notEqualTo(this, Expressions.Operand.NULL); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "in". - * - * @param operand - * The generic operands to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean in( @Nonnull final FilterableCollection operand ) - { - final ValueBoolean.Expression expression = FilterExpressionLogical.in(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableGuid.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableGuid.java deleted file mode 100644 index 03f3122da0..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableGuid.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import java.util.UUID; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Guid. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableGuid extends FilterableValue -{ - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableNumeric.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableNumeric.java deleted file mode 100644 index cdfe021737..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableNumeric.java +++ /dev/null @@ -1,340 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionArithmetic; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueNumeric; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Number. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableNumeric extends FilterableValue -{ - /** - * - * Filter by expression "add". - * - * @param operand - * The number to add. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric add( @Nonnull final Long operand ); - - /** - * - * Filter by expression "add". - * - * @param operand - * The number to add. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric add( @Nonnull final Integer operand ); - - /** - * - * Filter by expression "add". - * - * @param operand - * The number to add. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumericDecimal add( @Nonnull final Number operand ); - - /** - * - * Filter by expression "add". - * - * @param operand - * The number to add. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric add( @Nonnull final FilterableNumericInteger operand ); - - /** - * - * Filter by expression "add". - * - * @param operand - * The number to add. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericDecimal add( @Nonnull final FilterableNumericDecimal operand ) - { - final ValueNumeric thisNumeric = this::getExpression; - final ValueNumeric value = operand::getExpression; - final ValueNumeric.Expression expression = FilterExpressionArithmetic.add(thisNumeric, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "mul". - * - * @param operand - * The number to multiply. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric multiply( @Nonnull final Long operand ); - - /** - * - * Filter by expression "mul". - * - * @param operand - * The number to multiply. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric multiply( @Nonnull final Integer operand ); - - /** - * - * Filter by expression "mul". - * - * @param operand - * The number to multiply. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumericDecimal multiply( @Nonnull final Number operand ); - - /** - * - * Filter by expression "mul". - * - * @param operand - * The number to multiply. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric multiply( @Nonnull final FilterableNumericInteger operand ); - - /** - * - * Filter by expression "mul". - * - * @param operand - * The number to multiply. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericDecimal multiply( @Nonnull final FilterableNumericDecimal operand ) - { - final ValueNumeric thisNumeric = this::getExpression; - final ValueNumeric value = operand::getExpression; - final ValueNumeric.Expression expression = FilterExpressionArithmetic.multiply(thisNumeric, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "sub". - * - * @param operand - * The number to subtract. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric subtract( @Nonnull final Long operand ); - - /** - * - * Filter by expression "sub". - * - * @param operand - * The number to subtract. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric subtract( @Nonnull final Integer operand ); - - /** - * - * Filter by expression "sub". - * - * @param operand - * The number to subtract. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumericDecimal subtract( @Nonnull final Number operand ); - - /** - * - * Filter by expression "sub". - * - * @param operand - * The number to subtract. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric subtract( @Nonnull final FilterableNumericInteger operand ); - - /** - * - * Filter by expression "sub". - * - * @param operand - * The number to subtract. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericDecimal subtract( @Nonnull final FilterableNumericDecimal operand ) - { - final ValueNumeric thisNumeric = this::getExpression; - final ValueNumeric value = operand::getExpression; - final ValueNumeric.Expression expression = FilterExpressionArithmetic.subtract(thisNumeric, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "div". - * - * @param operand - * The number to divide. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericDecimal divide( @Nonnull final FilterableNumericInteger operand ) - { - final ValueNumeric thisNumeric = this::getExpression; - final ValueNumeric value = operand::getExpression; - final ValueNumeric.Expression expression = FilterExpressionArithmetic.divide(thisNumeric, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - /** - * Filter by expression "div". - * - * @param operand - * The number to divide. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericDecimal divide( @Nonnull final FilterableNumericDecimal operand ) - { - final ValueNumeric thisNumeric = this::getExpression; - final ValueNumeric value = operand::getExpression; - final ValueNumeric.Expression expression = FilterExpressionArithmetic.divide(thisNumeric, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "div". - * - * @param operand - * The number to divide. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericDecimal divide( @Nonnull final Number operand ) - { - final ValueNumeric thisNumeric = this::getExpression; - final ValueNumeric value = ValueNumeric.literal(operand); - final ValueNumeric.Expression expression = FilterExpressionArithmetic.divide(thisNumeric, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "mod". - * - * @param operand - * The base number to calculate the modulo from. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric modulo( @Nonnull final Long operand ); - - /** - * - * Filter by expression "mod". - * - * @param operand - * The base number to calculate the modulo from. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric modulo( @Nonnull final Integer operand ); - - /** - * - * Filter by expression "mod". - * - * @param operand - * The base number to calculate the modulo from. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumericDecimal modulo( @Nonnull final Number operand ); - - /** - * - * Filter by expression "mod". - * - * @param operand - * The base number to calculate the modulo from. - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric modulo( @Nonnull final FilterableNumericInteger operand ); - - /** - * - * Filter by expression "mod". - * - * @param operand - * The base number to calculate the modulo from. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericDecimal modulo( @Nonnull final FilterableNumericDecimal operand ) - { - final ValueNumeric thisNumeric = this::getExpression; - final ValueNumeric value = operand::getExpression; - final ValueNumeric.Expression expression = FilterExpressionArithmetic.modulo(thisNumeric, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "-". - * - * @return The FluentHelper filter. - */ - @Nonnull - FilterableNumeric negate(); -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableNumericDecimal.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableNumericDecimal.java deleted file mode 100644 index 7b1761008e..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableNumericDecimal.java +++ /dev/null @@ -1,249 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionArithmetic; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueNumeric; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Integer. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableNumericDecimal extends FilterableNumeric -{ - /** - * Wrapper expression class, which delegates to another operation. - * - * @param - * Type of the entity which references the value. - */ - @RequiredArgsConstructor - @Getter - class Expression implements FilterableNumericDecimal, FilterExpression - { - @Delegate - @Nonnull - private final FilterExpression delegate; - - @Nonnull - private final Class entityType; - } - - @Override - @Nonnull - default FilterableNumericDecimal add( @Nonnull final FilterableNumericInteger operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.add(thisNumber, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Nonnull - @Override - default FilterableNumericDecimal add( @Nonnull final Number operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.add(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal add( @Nonnull final Long operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.add(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal add( @Nonnull final Integer operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.add(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal multiply( @Nonnull final FilterableNumericInteger operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisNumber, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Nonnull - @Override - default FilterableNumericDecimal multiply( @Nonnull final Number operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal multiply( @Nonnull final Long operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal multiply( @Nonnull final Integer operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal subtract( @Nonnull final FilterableNumericInteger operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisNumber, value); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Nonnull - @Override - default FilterableNumericDecimal subtract( @Nonnull final Number operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal subtract( @Nonnull final Long operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal subtract( @Nonnull final Integer operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal modulo( @Nonnull final FilterableNumericInteger operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.modulo(thisNumber, value); - return new Expression<>(expression, getEntityType()); - } - - @Nonnull - @Override - default FilterableNumericDecimal modulo( @Nonnull final Number operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.modulo(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal modulo( @Nonnull final Long operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.modulo(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal modulo( @Nonnull final Integer operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.modulo(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericDecimal negate() - { - final ValueNumeric thisNumber = this::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.negate(thisNumber); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ceiling". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger ceil() - { - final ValueNumeric thisNumber = this::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.ceiling(thisNumber); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "floor". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger floor() - { - final ValueNumeric thisNumber = this::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.floor(thisNumber); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "round". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger round() - { - final ValueNumeric thisNumber = this::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.round(thisNumber); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableNumericInteger.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableNumericInteger.java deleted file mode 100644 index 344b171c82..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableNumericInteger.java +++ /dev/null @@ -1,207 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionArithmetic; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueNumeric; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Integer. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableNumericInteger extends FilterableNumeric -{ - /** - * Wrapper expression class, which delegates to another operation. - * - * @param - * Type of the entity which references the value. - */ - @RequiredArgsConstructor - @Getter - class Expression implements FilterableNumericInteger, FilterExpression - { - @Delegate - @Nonnull - private final FilterExpression delegate; - - @Nonnull - private final Class entityType; - } - - @Override - @Nonnull - default FilterableNumericInteger add( @Nonnull final FilterableNumericInteger operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.add(thisNumber, value); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - @Nonnull - @Override - default FilterableNumericDecimal add( @Nonnull final Number operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.add(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger add( @Nonnull final Long operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.add(thisNumber, literal); - return new Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger add( @Nonnull final Integer operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.add(thisNumber, literal); - return new Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger multiply( @Nonnull final FilterableNumericInteger operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisNumber, value); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - @Nonnull - @Override - default FilterableNumericDecimal multiply( @Nonnull final Number operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger multiply( @Nonnull final Long operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisNumber, literal); - return new Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger multiply( @Nonnull final Integer operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.multiply(thisNumber, literal); - return new Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger subtract( @Nonnull final FilterableNumericInteger operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisNumber, value); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - @Nonnull - @Override - default FilterableNumericDecimal subtract( @Nonnull final Number operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger subtract( @Nonnull final Long operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisNumber, literal); - return new Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger subtract( @Nonnull final Integer operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.subtract(thisNumber, literal); - return new Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger modulo( @Nonnull final FilterableNumericInteger operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric value = operand::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.modulo(thisNumber, value); - return new Expression<>(expression, getEntityType()); - } - - @Nonnull - @Override - default FilterableNumericDecimal modulo( @Nonnull final Number operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.modulo(thisNumber, literal); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger modulo( @Nonnull final Long operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.modulo(thisNumber, literal); - return new Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger modulo( @Nonnull final Integer operand ) - { - final ValueNumeric thisNumber = this::getExpression; - final ValueNumeric literal = ValueNumeric.literal(operand); - final FilterExpression expression = FilterExpressionArithmetic.modulo(thisNumber, literal); - return new Expression<>(expression, getEntityType()); - } - - @Override - @Nonnull - default FilterableNumericInteger negate() - { - final ValueNumeric thisNumber = this::getExpression; - final FilterExpression expression = FilterExpressionArithmetic.negate(thisNumber); - return new Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableString.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableString.java deleted file mode 100644 index f87b5200b4..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableString.java +++ /dev/null @@ -1,336 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.Expressions; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionString; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueNumeric; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueString; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by String. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableString extends FilterableValue -{ - /** - * Wrapper expression class, which delegates to another operation. - * - * @param - * Type of the entity which references the value. - */ - @RequiredArgsConstructor - @Getter - class Expression implements FilterableString, FilterExpression - { - @Delegate - @Nonnull - private final FilterExpression delegate; - - @Nonnull - private final Class entityType; - } - - /** - * - * Filter by expression "matchesPattern". - * - * @param operand - * String expression to match the string against. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean matches( @Nonnull final String operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = (ValueString) Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionString.matchesPattern(thisString, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "matchesPattern". - * - * @param operand - * String expression to match the string against. - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean matches( @Nonnull final FilterableString operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = operand::getExpression; - final ValueBoolean.Expression expression = FilterExpressionString.matchesPattern(thisString, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "tolower". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableString toLower() - { - final ValueString.Expression expression = FilterExpressionString.toLower(this::getExpression); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "toupper". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableString toUpper() - { - final ValueString.Expression expression = FilterExpressionString.toUpper(this::getExpression); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "trim". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableString trim() - { - final ValueString.Expression expression = FilterExpressionString.trim(this::getExpression); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "length". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger length() - { - final ValueNumeric.Expression expression = FilterExpressionString.length(this::getExpression); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "concat". - * - * @param operand - * The string to concatenate with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableString concat( @Nonnull final FilterableString operand ) - { - final ValueString value = operand::getExpression; - final ValueString.Expression expression = FilterExpressionString.concat(this::getExpression, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "concat". - * - * @param operand - * The string to concatenate with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableString concat( @Nonnull final String operand ) - { - final ValueString value = (ValueString) Expressions.createOperand(operand); - final ValueString.Expression expression = FilterExpressionString.concat(this::getExpression, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "startswith". - * - * @param operand - * The substring which is checked for. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean startsWith( @Nonnull final FilterableString operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = operand::getExpression; - final ValueBoolean.Expression expression = FilterExpressionString.startsWith(thisString, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "startswith". - * - * @param operand - * The substring which is checked for. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean startsWith( @Nonnull final String operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = (ValueString) Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionString.startsWith(thisString, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "endswith". - * - * @param operand - * The substring which is checked for. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean endsWith( @Nonnull final FilterableString operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = operand::getExpression; - final ValueBoolean.Expression expression = FilterExpressionString.endsWith(thisString, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "endswith". - * - * @param operand - * The substring which is checked for. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean endsWith( @Nonnull final String operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = (ValueString) Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionString.endsWith(thisString, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "contain". - * - * @param operand - * The substring which is checked for. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean contains( @Nonnull final FilterableString operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = operand::getExpression; - final ValueBoolean.Expression expression = FilterExpressionString.contains(thisString, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "contain". - * - * @param operand - * The substring which is checked for. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean contains( @Nonnull final String operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = (ValueString) Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionString.contains(thisString, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "indexof". - * - * @param operand - * The substring which is checked for. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger indexOf( @Nonnull final FilterableString operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = operand::getExpression; - final ValueNumeric.Expression expression = FilterExpressionString.indexOf(thisString, value); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "indexof". - * - * @param operand - * The substring which is checked for. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger indexOf( @Nonnull final String operand ) - { - final ValueString thisString = this::getExpression; - final ValueString value = (ValueString) Expressions.createOperand(operand); - final ValueNumeric.Expression expression = FilterExpressionString.indexOf(thisString, value); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "substring". - * - * @param operand - * The number of characters to cut off. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableString substring( @Nonnull final Integer operand ) - { - final ValueString thisString = this::getExpression; - final ValueNumeric value = (ValueNumeric) Expressions.createOperand(operand); - final ValueString.Expression expression = FilterExpressionString.substring(thisString, value); - return new Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "substring". - * - * @param operandIndex - * The number of characters to cut off. - * @param operandLength - * The number of characters to keep in. - * @return The FluentHelper filter. - */ - @Nonnull - default - FilterableString - substring( @Nonnull final Integer operandIndex, @Nonnull final Integer operandLength ) - { - final ValueString thisString = this::getExpression; - final ValueNumeric index = (ValueNumeric) Expressions.createOperand(operandIndex); - final ValueNumeric length = (ValueNumeric) Expressions.createOperand(operandLength); - final ValueString.Expression expression = FilterExpressionString.substring(thisString, index, length); - return new Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableTime.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableTime.java deleted file mode 100644 index 57fc39c9ae..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableTime.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import java.time.LocalTime; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionTemporal; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueTimeOfDay; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Delegate; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by Time. - * - * @param - * Type of the entity which references the value. - */ -public interface FilterableTime extends FilterableValue -{ - /** - * Wrapper expression class, which delegates to another operation. - * - * @param - * Type of the entity which references the value. - */ - @RequiredArgsConstructor - @Getter - class Expression implements FilterableTime, FilterExpression - { - @Delegate - @Nonnull - private final FilterExpression delegate; - - @Nonnull - private final Class entityType; - } - - /** - * - * Filter by expression "fractionalseconds". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericDecimal timeFractionalSeconds() - { - final ValueTimeOfDay thisTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.fractionalSeconds(thisTime); - return new FilterableNumericDecimal.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "second". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger timeSecond() - { - final ValueTimeOfDay thisTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.second(thisTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "minute". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger timeMinute() - { - final ValueTimeOfDay thisTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.minute(thisTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "hour". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableNumericInteger timeHour() - { - final ValueTimeOfDay thisTime = this::getExpression; - final FilterExpression expression = FilterExpressionTemporal.hour(thisTime); - return new FilterableNumericInteger.Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableValue.java b/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableValue.java deleted file mode 100644 index b28bf441ce..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/main/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableValue.java +++ /dev/null @@ -1,284 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import java.io.Serializable; -import java.util.Arrays; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.Expressions; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpressionLogical; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; - -/** - * Fluent helper class to provide filter functions to OData expressions referenced by all value types. - * - * @param - * Type of the entity which references the value. - * @param - * Type of the value the filterable field holds. - */ -public interface FilterableValue - extends - Expressions.Operand, - EntityReference -{ - /** - * - * Filter by expression "eq null". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean equalToNull() - { - final ValueBoolean.Expression expression = FilterExpressionLogical.equalTo(this, Expressions.Operand.NULL); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ne null". - * - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean notEqualToNull() - { - final ValueBoolean.Expression expression = FilterExpressionLogical.notEqualTo(this, Expressions.Operand.NULL); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "eq". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean equalTo( @Nonnull final FilterableValue operand ) - { - final ValueBoolean.Expression expression = FilterExpressionLogical.equalTo(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "eq". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - * @throws IllegalArgumentException - * When there is no mapping found for the provided Java literal. - */ - @Nonnull - default FilterableBoolean equalTo( @Nullable final PrimitiveT operand ) - { - final Expressions.Operand value = Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionLogical.equalTo(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ne". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean notEqualTo( @Nonnull final FilterableValue operand ) - { - final ValueBoolean.Expression expression = FilterExpressionLogical.notEqualTo(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ne". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - * @throws IllegalArgumentException - * When there is no mapping found for the provided Java literal. - */ - @Nonnull - default FilterableBoolean notEqualTo( @Nullable final PrimitiveT operand ) - { - final Expressions.Operand value = Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionLogical.notEqualTo(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "lt". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean lessThan( @Nonnull final FilterableValue operand ) - { - final ValueBoolean.Expression expression = FilterExpressionLogical.lessThan(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "lt". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - * @throws IllegalArgumentException - * When there is no mapping found for the provided Java literal. - */ - @Nonnull - default FilterableBoolean lessThan( @Nonnull final PrimitiveT operand ) - { - final Expressions.Operand value = Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionLogical.lessThan(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "le". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean lessThanEqual( @Nonnull final FilterableValue operand ) - { - final ValueBoolean.Expression expression = FilterExpressionLogical.lessThanEquals(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "le". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - * @throws IllegalArgumentException - * When there is no mapping found for the provided Java literal. - */ - @Nonnull - default FilterableBoolean lessThanEqual( @Nonnull final PrimitiveT operand ) - { - final Expressions.Operand value = Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionLogical.lessThanEquals(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "gt". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean greaterThan( @Nonnull final FilterableValue operand ) - { - final ValueBoolean.Expression expression = FilterExpressionLogical.greaterThan(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "gt". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - * @throws IllegalArgumentException - * When there is no mapping found for the provided Java literal. - */ - @Nonnull - default FilterableBoolean greaterThan( @Nonnull final PrimitiveT operand ) - { - final Expressions.Operand value = Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionLogical.greaterThan(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ge". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean greaterThanEqual( @Nonnull final FilterableValue operand ) - { - final ValueBoolean.Expression expression = FilterExpressionLogical.greaterThanEquals(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "ge". - * - * @param operand - * The generic operand to compare with. - * @return The FluentHelper filter. - * @throws IllegalArgumentException - * When there is no mapping found for the provided Java literal. - */ - @Nonnull - default FilterableBoolean greaterThanEqual( @Nonnull final PrimitiveT operand ) - { - final Expressions.Operand value = Expressions.createOperand(operand); - final ValueBoolean.Expression expression = FilterExpressionLogical.greaterThanEquals(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "in". - * - * @param operands - * The generic operands to compare with. - * @return The FluentHelper filter. - * @throws IllegalArgumentException - * When there is no mapping found for any of the provided Java literals. - */ - @SuppressWarnings( "unchecked" ) - @Nonnull - default FilterableBoolean in( @Nonnull final PrimitiveT... operands ) - { - final Expressions.Operand[] value = - Arrays.stream(operands).map(Expressions::createOperand).toArray(Expressions.Operand[]::new); - final ValueBoolean.Expression expression = FilterExpressionLogical.in(this, value); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } - - /** - * - * Filter by expression "in". - * - * @param operand - * The generic operands to compare with. - * @return The FluentHelper filter. - */ - @Nonnull - default FilterableBoolean in( @Nonnull final FilterableCollection operand ) - { - final ValueBoolean.Expression expression = FilterExpressionLogical.in(this, operand); - return new FilterableBoolean.Expression<>(expression, getEntityType()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/TestUtility.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/TestUtility.java deleted file mode 100644 index 352e18c032..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/TestUtility.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4; - -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; - -import com.google.common.io.Resources; -import com.sap.cloud.sdk.testutil.TestConfigurationError; - -public class TestUtility -{ - public static String readResourceFile( final Class cls, final String resourceFileName ) - { - try { - final URL resourceUrl = getResourceUrl(cls, resourceFileName); - - return Resources.toString(resourceUrl, StandardCharsets.UTF_8); - } - catch( final IOException e ) { - throw new TestConfigurationError(e); - } - } - - public static String readResourceFileCrlf( final Class cls, final String resourceFileName ) - { - return readResourceFile(cls, resourceFileName).replaceAll("(? cls, final String resourceFileName ) - { - final URL resourceUrl = cls.getClassLoader().getResource(cls.getSimpleName() + "/" + resourceFileName); - - if( resourceUrl == null ) { - throw new TestConfigurationError("Cannot find resource file with name \"" + resourceFileName + "\"."); - } - - return resourceUrl; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/BigDecimalSerialisationTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/BigDecimalSerialisationTest.java deleted file mode 100644 index 8782869949..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/BigDecimalSerialisationTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.math.BigDecimal; - -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.gson.Gson; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -class BigDecimalSerialisationTest -{ - private static final String TEST_DEFAULT_SERVICE_PATH = "/odata/default"; - - @NoArgsConstructor - @JsonAdapter( GsonVdmAdapterFactory.class ) - @JsonSerialize( using = JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = JacksonVdmObjectDeserializer.class ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @Data - private class TestEntity extends VdmEntity - { - @Getter - private final String odataType = "TestEntity"; - - @Getter - private final String entityCollection = "EntityCollection"; - - @Getter - private final Class type = TestEntity.class; - - @Getter - private final String defaultServicePath = TEST_DEFAULT_SERVICE_PATH; - - @ElementName( "BigDecimalField" ) - BigDecimal bigDecimalField; - - } - - @Test - void testSerialization() - throws JsonProcessingException - { - final TestEntity testEntity = new TestEntity(); - testEntity.setBigDecimalField(new BigDecimal("0.000000003")); - // GSON - final String jsonGson = new Gson().toJson(testEntity); - assertThat(jsonGson).doesNotContain("3E-9").contains("0.000000003"); - // Jackson - final String jsonJackson = new ObjectMapper().writeValueAsString(testEntity); - assertThat(jsonJackson).doesNotContain("3E-9").contains("0.000000003"); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/SerializerAdapterTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/SerializerAdapterTest.java deleted file mode 100644 index 8200f547b5..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/adapter/SerializerAdapterTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.adapter; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.OffsetDateTime; -import java.util.Collections; -import java.util.UUID; - -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.City; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Feature; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Location; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Trip; - -class SerializerAdapterTest -{ - private static final City CITY = - City.builder().name("Potsdam").countryRegion("Brandenburg").region("Deutschland").build(); - - private static final Trip TRIP_A = - Trip.builder().budget(100.0f).endsAt(OffsetDateTime.now()).shareId(UUID.randomUUID()).name("a").build(); - - private static final Trip TRIP_B = - Trip.builder().budget(20.0f).endsAt(OffsetDateTime.now()).shareId(UUID.randomUUID()).name("b").build(); - - private static final Person PERSON = - Person - .builder() - .lastName("Bar") - .emails(Collections.singletonList("foo@bar.com")) - .favoriteFeature(Feature.FEATURE1) - .trips(TRIP_A, TRIP_B) - .addressInfo(Collections.singletonList(new Location("Sesamstr.1", CITY))) - .build(); - - @Test - void testCompatibleNonNullGsonAndJackson() - throws JsonProcessingException - { - // GSON - final Gson gson = new Gson(); - final String jsonGson = gson.toJson(PERSON); - final Person personGson = gson.fromJson(jsonGson, Person.class); - - // Jackson - final ObjectMapper mapper = new ObjectMapper(); - mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL); - final String jsonJackson = mapper.writeValueAsString(PERSON); - final Person personJackson = mapper.readValue(jsonJackson, Person.class); - - // test - assertThat(personJackson).isEqualTo(personGson); - } - - @Test - void testCompatibleNullableGsonAndJackson() - throws JsonProcessingException - { - // GSON - final Gson gson = new GsonBuilder().serializeNulls().create(); - final String jsonGson = gson.toJson(PERSON); - final Person personGson = gson.fromJson(jsonGson, Person.class); - - // Jackson - final ObjectMapper mapper = new ObjectMapper(); - final String jsonJackson = mapper.writeValueAsString(PERSON); - final Person personJackson = mapper.readValue(jsonJackson, Person.class); - - // test - assertThat(personJackson).isEqualTo(personGson); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundActionTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundActionTest.java deleted file mode 100644 index 8837a419e6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundActionTest.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; - -import org.apache.hc.core5.http.HttpHeaders; -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Location; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class BoundActionTest -{ - private static final DefaultTrippinService service = new DefaultTrippinService().withServicePath("service-root"); - private static final Person person = Person.builder().userName("Fridolin").build(); - - @Test - void testActionOnEntityWithEtag() - { - final String targetUrl = "/service-root/People('Fridolin')/Trippin.MakeHappy"; - - person.setVersionIdentifier("some-etag"); - final SingleValueActionRequestBuilder action = service.forEntity(person).applyAction(Person.makeHappy()); - - assertThat(action.toRequest().getRelativeUri()).hasToString(targetUrl); - assertThat(action.toRequest().getActionParameters()).hasToString("{}"); - assertThat(action.toRequest().getHeaders()) - .containsEntry(HttpHeaders.IF_MATCH, Collections.singletonList("some-etag")); - } - - @Test - void testActionOnEntityNoEtag() - { - final String targetUrl = "/service-root/People('Fridolin')/Trippin.MakeHappy"; - - person.setVersionIdentifier(null); - final SingleValueActionRequestBuilder action = service.forEntity(person).applyAction(Person.makeHappy()); - - assertThat(action.toRequest().getRelativeUri()).hasToString(targetUrl); - assertThat(action.toRequest().getActionParameters()).hasToString("{}"); - assertThat(action.toRequest().getHeaders()).doesNotContainKey(HttpHeaders.IF_MATCH); - } - - @Test - void testActionWithParameters() - { - final String targetUrl = "/service-root/People('Fridolin')/Trippin.MakeUnhappy"; - - final SingleValueActionRequestBuilder action = - service.forEntity(person).applyAction(Person.makeUnhappy(true)); - - assertThat(action.toRequest().getRelativeUri()).hasToString(targetUrl); - assertThat(action.toRequest().getActionParameters()).hasToString("{\"very\":true}"); - } - - @Test - void testActionSingleToCollection() - { - final String targetUrl = "/service-root/People/Trippin.MakeAllHappy"; - - final SingleValueActionRequestBuilder action = service.applyAction(Person.makeAllHappy()); - - assertThat(action.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testActionCollectionToCollection() - { - final String targetUrl = "/service-root/People('Fridolin')/Trippin.sendMail"; - - final CollectionValueActionRequestBuilder action = - service.forEntity(person).applyAction(Person.sendMail("foo")); - - assertThat(action.toRequest().getActionParameters()).isEqualTo("{\"subject\":\"foo\"}"); - assertThat(action.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testActionOnNestedCollection() - { - final String targetUrl = "/service-root/People('Fridolin')/Friends/Trippin.MakeAllHappy"; - - final SingleValueActionRequestBuilder action = - service.forEntity(person).navigateTo(Person.TO_FRIENDS).applyAction(Person.makeAllHappy()); - - assertThat(action.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testSuccessfulCompilation() - { - // test to ensure that all the combinations at least compile - - // 1 - 1 - final SingleValueActionRequestBuilder action1 = - service - .forEntity(person) - .applyAction( - new BoundAction.SingleToSingle<>(Person.class, Location.class, "Stuff", Collections.emptyMap())); - - // 1 - n - final CollectionValueActionRequestBuilder action2 = - service - .forEntity(person) - .applyAction( - new BoundAction.SingleToCollection<>(Person.class, String.class, "Stuff", Collections.emptyMap())); - - // n - 1 - final SingleValueActionRequestBuilder action3 = - service - .applyAction( - new BoundAction.CollectionToSingle<>(Person.class, String.class, "Stuff", Collections.emptyMap())); - - // n - n - final CollectionValueActionRequestBuilder action4 = - service - .applyAction( - new BoundAction.CollectionToCollection<>( - Person.class, - String.class, - "Stuff", - Collections.emptyMap())); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundFunctionTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundFunctionTest.java deleted file mode 100644 index 3d48926d42..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/BoundFunctionTest.java +++ /dev/null @@ -1,331 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Location; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class BoundFunctionTest -{ - private static final DefaultTrippinService service = new DefaultTrippinService().withServicePath("service-root"); - private static final Person person = Person.builder().userName("Fridolin").build(); - - /* From OData V4 ABNF - - ; boundOperation segments can only be composed if the type of the previous segment - ; matches the type of the first parameter of the action or function being called. - ; Note that the rule name reflects the return type of the function. - boundOperation = "/" ( boundActionCall - / boundEntityColFunctionCall [ collectionNavigation ] - / boundEntityFunctionCall [ singleNavigation ] - / boundComplexColFunctionCall [ complexColPath ] - / boundComplexFunctionCall [ complexPath ] - / boundPrimitiveColFunctionCall [ primitiveColPath ] - / boundPrimitiveFunctionCall [ primitivePath ] - / boundFunctionCallNoParens - ) - */ - - // Problem: We don't have a good abstraction for entity sets - // Consequence: forEntity(..) doesn't work if there is more than 1 entity set for the type - // the same problem has this approach - - @Test - void testEntityToPrimitive() - { - final String targetUrl = "/service-root/People('Fridolin')/Trippin.IsHappy()"; - - final SingleValueFunctionRequestBuilder function = - service.forEntity(person).applyFunction(Person.isHappy()); - - assertThat(function.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testNavigationToPrimitive() - { - final String targetUrl = "/service-root/People('Fridolin')/BestFriend/Trippin.IsHappy()"; - - final SingleValueFunctionRequestBuilder function = - service.forEntity(person).navigateTo(Person.TO_BEST_FRIEND).applyFunction(Person.isHappy()); - - assertThat(function.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testCollectionToPrimitive() - { - final String targetUrl = "/service-root/People('Fridolin')/Friends/Trippin.AreAllFriends()"; - - final SingleValueFunctionRequestBuilder function = - service.forEntity(person).navigateTo(Person.TO_FRIENDS).applyFunction(Person.areAllFriends()); - - assertThat(function.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testCollectionToCollection() - { - final String targetUrl = "/service-root/People/Trippin.MostPopularPersons()"; - - final CollectionValueFunctionRequestBuilder function = - service.applyFunction(Person.mostPopularPersons()); - - assertThat(function.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testCollectionOnRootEntity() - { - final String targetUrl = "/service-root/People/Trippin.AreAllFriends()"; - - final SingleValueFunctionRequestBuilder function = service.applyFunction(Person.areAllFriends()); - - assertThat(function.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testNavigationCollectionToPrimitive() - { - final String targetUrl = "/service-root/People('Fridolin')/Friends/Trippin.AreAllFriends()"; - - final SingleValueFunctionRequestBuilder function = - service.forEntity(person).navigateTo(Person.TO_FRIENDS).applyFunction(Person.areAllFriends()); - - assertThat(function.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testEntityToEntity() - { - final String targetUrl = "/service-root/People('Fridolin')/Trippin.WorstFriend()"; - - final SingleValueFunctionRequestBuilder function = - service.forEntity(person).applyFunction(Person.worstFriend()); - - assertThat(function.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testCompositionOnEntity() - { - final String targetUrl = "/service-root/People('Fridolin')/Trippin.WorstFriend()/Friends?$top=5"; - - final GetAllRequestBuilder request = - service.forEntity(person).withFunction(Person.worstFriend()).navigateTo(Person.TO_FRIENDS).getAll().top(5); - - assertThat(request.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testCompositionOnRootEntity() - { - final String targetUrl = "/service-root/People/Trippin.MostPopularPerson()/Friends?$top=5"; - - final GetAllRequestBuilder request = - service.withFunction(Person.mostPopularPerson()).navigateTo(Person.TO_FRIENDS).getAll().top(5); - - assertThat(request.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testGetAllEntitiesAfterFunctionResult() - { - final String targetUrl = "/service-root/People/Trippin.MostPopularPersons()"; - - final GetAllRequestBuilder request = service.withFunction(Person.mostPopularPersons()).getAll(); - - assertThat(request.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testSubsequentFunctionInvocation() - { - final String targetUrl = "/service-root/People/Trippin.MostPopularPersons()/Trippin.AreAllFriends()"; - - final SingleValueFunctionRequestBuilder request = - service.withFunction(Person.mostPopularPersons()).applyFunction(Person.areAllFriends()); - - assertThat(request.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testAdditionalParameters() - { - final String targetUrl = "/service-root/People('Fridolin')/Trippin.IsHappy(really=true)"; - - final SingleValueFunctionRequestBuilder function = - service.forEntity(person).applyFunction(Person.isHappy(true)); - - assertThat(function.toRequest().getRelativeUri()).hasToString(targetUrl); - } - - @Test - void testSuccessfulCompilation() - { - // test to ensure that all the combinations at least compile - - // 1 - 1 - SingleValueFunctionRequestBuilder fun1; - fun1 = - service - .forEntity(person) - .applyFunction( - new BoundFunction.SingleToSingleComplex<>( - Person.class, - Location.class, - "Stuff", - Collections.emptyMap())); - fun1 = - service - .forEntity(person) - .applyFunction( - new BoundFunction.SingleToSingleEntity<>( - Person.class, - Person.class, - "Stuff", - Collections.emptyMap())); - fun1 = - service - .forEntity(person) - .applyFunction( - new BoundFunction.SingleToSinglePrimitive<>( - Person.class, - String.class, - "Stuff", - Collections.emptyMap())); - - // 1 - n - CollectionValueFunctionRequestBuilder fun2; - fun2 = - service - .forEntity(person) - .applyFunction( - new BoundFunction.SingleToCollectionPrimitive<>( - Person.class, - String.class, - "Stuff", - Collections.emptyMap())); - fun2 = - service - .forEntity(person) - .applyFunction( - new BoundFunction.SingleToCollectionComplex<>( - Person.class, - Location.class, - "Stuff", - Collections.emptyMap())); - fun2 = - service - .forEntity(person) - .applyFunction( - new BoundFunction.SingleToCollectionEntity<>( - Person.class, - Person.class, - "Stuff", - Collections.emptyMap())); - - // n - 1 - SingleValueFunctionRequestBuilder fun3; - fun3 = - service - .applyFunction( - new BoundFunction.CollectionToSinglePrimitive<>( - Person.class, - String.class, - "Stuff", - Collections.emptyMap())); - fun3 = - service - .applyFunction( - new BoundFunction.CollectionToSingleComplex<>( - Person.class, - Location.class, - "Stuff", - Collections.emptyMap())); - fun3 = - service - .applyFunction( - new BoundFunction.CollectionToSingleEntity<>( - Person.class, - Person.class, - "Stuff", - Collections.emptyMap())); - - // n - n - CollectionValueFunctionRequestBuilder fun4; - fun2 = - service - .applyFunction( - new BoundFunction.CollectionToCollectionPrimitive<>( - Person.class, - String.class, - "Stuff", - Collections.emptyMap())); - fun2 = - service - .applyFunction( - new BoundFunction.CollectionToCollectionComplex<>( - Person.class, - Location.class, - "Stuff", - Collections.emptyMap())); - fun2 = - service - .applyFunction( - new BoundFunction.CollectionToCollectionEntity<>( - Person.class, - Person.class, - "Stuff", - Collections.emptyMap())); - - } - - /* - @Test - void testBoundFunctionOnPrimitiveType() - { - // Target URI: /service-root/People('Fridolin')/FirstName/Model.String.ToUpperCase() - - SingleValueFunctionRequestBuilder function = - service - .forEntity(person) - // overload this to support both primitive and complex types - .onProperty(Person.FIRST_NAME) - // Function is of type: String -> String - .applyFunction(TrippinService.PrimitiveFunctions.toUpperCase()); - }*/ - - /* - @Test - void testBoundFunctionOnComplexType() - { - // Target URI: /service-root/People('Fridolin')/AddressInfo/Model.Address.GetClosestLocation() - - SingleValueFunctionRequestBuilder function = - service - .forEntity(person) - .onProperty(Person.ADDRESS_INFO) - // Function is of type: Collection -> Location - .applyFunction(Location.getClosestLocation()); - }*/ - - /* - @Test - void testBoundFunctionOnEachEntityInCollection() - { - // Target URI: /service-root/People/$each/Trippin.isHappy() - - SingleValueFunctionRequestBuilder function = - service - // overload this to support both functions on single entities and collections - // automatically apply $each if the function is of type: EntityT -> Any - .applyFunction(Person.isHappy()); - - }*/ -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueActionRequestBuilderTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueActionRequestBuilderTest.java deleted file mode 100644 index c00f9fb29b..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueActionRequestBuilderTest.java +++ /dev/null @@ -1,236 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odatav4.TestUtility; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@WireMockTest -class CollectionValueActionRequestBuilderTest -{ - private static final String DEFAULT_SERVICE_PATH = "/odata/default"; - private static final String ODATA_ACTION = "TestAction"; - - private DefaultHttpDestination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - stubFor(head(anyUrl()).willReturn(ok())); - } - - private static String readResourceFile( final String resourceFileName ) - { - return TestUtility.readResourceFile(CollectionValueActionRequestBuilderTest.class, resourceFileName); - } - - @Data - @EqualsAndHashCode( callSuper = true ) - @NoArgsConstructor - @AllArgsConstructor - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - @JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) - public static class TestEntity extends VdmEntity - { - @Getter - private final String odataType = "com.sap.cloud.sdk.TestEntity"; - - @Getter - private final String entityCollection = "EntityCollection"; - - @Getter - private final Class type = - SingleValueFunctionRequestBuilderTest.TestEntity.class; - - @Override - protected String getDefaultServicePath() - { - return DEFAULT_SERVICE_PATH; - } - - @ElementName( "Name" ) - @SerializedName( "Name" ) - @JsonProperty( "Name" ) - private String name; - } - - @Builder - @Data - @NoArgsConstructor - @AllArgsConstructor - @ToString( doNotUseGetters = true, callSuper = true ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - @JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) - public static class ComplexType extends VdmComplex - { - - @Getter - private final String odataType = "com.sap.cloud.sdk.ComplexType"; - - @Getter - private final Class type = - SingleValueActionRequestBuilderTest.ComplexType.class; - - @ElementName( "City" ) - private String city; - - @ElementName( "Country" ) - private String country; - } - - @Test - void testActionWithPrimitiveResponse() - { - stubFor( - post(urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)) - .willReturn(okJson("{\"value\" : [ 3.14, 9.81 ]}"))); - - final CollectionValueActionRequestBuilder sut = - new CollectionValueActionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_ACTION, Float.class); - final String actionRequestBody = readResourceFile("ActionNoParametersRequestBody.json"); - - final ActionResponseCollection actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)) - .withHeader("Content-Type", equalTo("application/json")) - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult().get()).hasSize(2); - assertThat(actualResponse.getResponseResult().get()).contains(3.14f, 9.81f); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(200); - } - - @Test - void testActionWithStringResponse() - { - - stubFor( - post(urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)) - .willReturn(okJson("{" + "\"value\" : [ \"It\", \"works\",\"as\",\"expected\" ]" + "}"))); - - final CollectionValueActionRequestBuilder sut = - new CollectionValueActionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_ACTION, String.class); - final String actionRequestBody = readResourceFile("ActionNoParametersRequestBody.json"); - - final ActionResponseCollection actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)) - .withHeader("Content-Type", equalTo("application/json")) - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult().get()).hasSize(4); - assertThat(actualResponse.getResponseResult().get()).contains("It", "works", "as", "expected"); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(200); - } - - @Test - void testActionWithComplexTypeResponse() - { - - stubFor(post(urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)).willReturn(okJson(""" - { "value" : [\ - { "City" : "Stockholm" ,"Country" : "Sweden"},\ - { "City" : "Dubrovnik","Country" : "Croatia" }\ - ]}\ - """))); - - final CollectionValueActionRequestBuilder sut = - new CollectionValueActionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_ACTION, ComplexType.class); - final String actionRequestBody = readResourceFile("ActionNoParametersRequestBody.json"); - final ActionResponseCollection actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)) - .withHeader("Content-Type", equalTo("application/json")) - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult().get()).hasSize(2); - assertThat(actualResponse.getResponseResult().get()) - .contains(new ComplexType("Stockholm", "Sweden"), new ComplexType("Dubrovnik", "Croatia")); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(200); - } - - @Test - void testActionWithEntityResponse() - { - stubFor( - post(urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)) - .willReturn( - okJson("{ \"value\" : [" + "{ \"Name\" : \"Tester1\" }," + "{ \"Name\" : \"Tester2\" }" + "]}"))); - - final CollectionValueActionRequestBuilder sut = - new CollectionValueActionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_ACTION, TestEntity.class); - final String actionRequestBody = readResourceFile("ActionNoParametersRequestBody.json"); - final ActionResponseCollection actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)) - .withHeader("Content-Type", equalTo("application/json")) - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult().get()).hasSize(2); - assertThat(actualResponse.getResponseResult().get()) - .contains(new TestEntity("Tester1"), new TestEntity("Tester2")); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(200); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueFunctionRequestBuilderTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueFunctionRequestBuilderTest.java deleted file mode 100644 index 320c05d77a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CollectionValueFunctionRequestBuilderTest.java +++ /dev/null @@ -1,182 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Duration; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataFunctionParameters; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@WireMockTest -class CollectionValueFunctionRequestBuilderTest -{ - private static final String SERVICE_PATH = "/odata/default"; - private static final String FUNCTION_NAME = "TestFunction"; - private static final ODataFunctionParameters FUNCTION_PARAMETERS; - private static final Map FUNCTION_PARAMETER_MAP = new HashMap<>(); - - static { - FUNCTION_PARAMETER_MAP.put("stringParameter", "test"); - FUNCTION_PARAMETER_MAP.put("booleanParameter", true); - FUNCTION_PARAMETER_MAP.put("integerParameter", 9000); - FUNCTION_PARAMETER_MAP.put("decimalParameter", 3.14); - FUNCTION_PARAMETER_MAP.put("durationParameter", Duration.ofHours(8)); - FUNCTION_PARAMETER_MAP.put("dateTimeParameter", LocalDateTime.of(2019, 12, 25, 8, 0, 0)); - - FUNCTION_PARAMETERS = ODataFunctionParameters.of(FUNCTION_PARAMETER_MAP, ODataProtocol.V4); - } - - private static final String ODATA_FUNCTION_PARAMETER_WITH_SPECIAL_CHARACTER = "(stringParameter='t''est')"; - - private DefaultHttpDestination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - } - - @Data - @EqualsAndHashCode( callSuper = true ) - @NoArgsConstructor - @AllArgsConstructor - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - @JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) - public static class TestEntity extends VdmEntity - { - @Getter - private final String odataType = "com.sap.cloud.sdk.TestEntity"; - - @Getter - private final String entityCollection = "EntityCollection"; - - @Getter - private final Class type = - SingleValueFunctionRequestBuilderTest.TestEntity.class; - - @Override - protected String getDefaultServicePath() - { - return SERVICE_PATH; - } - - @ElementName( "Name" ) - @SerializedName( "Name" ) - @JsonProperty( "Name" ) - private String name; - } - - @Test - void testFunctionQueryWithoutParameters() - { - final CollectionValueFunctionRequestBuilder requestBuilder = - new CollectionValueFunctionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, Void.class); - - assertThat(requestBuilder.toRequest().getRelativeUri()).hasToString(SERVICE_PATH + '/' + FUNCTION_NAME + "()"); - } - - @Test - void testFunctionQueryWithMethodParameters() - { - final CollectionValueFunctionRequestBuilder requestBuilder = - new CollectionValueFunctionRequestBuilder( - SERVICE_PATH, - FUNCTION_NAME, - FUNCTION_PARAMETER_MAP, - Void.class); - - assertThat(requestBuilder.toRequest().getRelativeUri()) - .hasToString(SERVICE_PATH + '/' + FUNCTION_NAME + FUNCTION_PARAMETERS.toEncodedString()); - } - - @Test - void testFunctionQueryWithSpecialCharactersInMethodParameters() - { - final ODataFunctionParameters parameters = - new ODataFunctionParameters(ODataProtocol.V4).addParameter("stringParameter", "t'est"); - final ODataResourcePath functionPath = ODataResourcePath.of(FUNCTION_NAME, parameters); - - final CollectionValueFunctionRequestBuilder requestBuilder = - new CollectionValueFunctionRequestBuilder<>(SERVICE_PATH, functionPath, Void.class); - - assertThat(requestBuilder.toRequest().getRelativeUri()) - .hasToString(SERVICE_PATH + '/' + FUNCTION_NAME + ODATA_FUNCTION_PARAMETER_WITH_SPECIAL_CHARACTER); - } - - @Test - void testFunctionQueryWithMapParameters() - { - final CollectionValueFunctionRequestBuilder requestBuilder = - new CollectionValueFunctionRequestBuilder<>( - SERVICE_PATH, - FUNCTION_NAME, - FUNCTION_PARAMETER_MAP, - Void.class); - - assertThat(requestBuilder.toRequest().getRelativeUri()) - .hasToString(SERVICE_PATH + '/' + FUNCTION_NAME + FUNCTION_PARAMETERS.toEncodedString()); - } - - @Test - void testFunctionWithPrimitiveResponse() - { - stubFor( - get(urlPathEqualTo(SERVICE_PATH + '/' + FUNCTION_NAME + "()")) - .willReturn(okJson("{" + "\"value\" : [ 3.14f, 9.81f ]" + "}"))); - - final CollectionValueFunctionRequestBuilder requestBuilder = - new CollectionValueFunctionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, Float.class); - - final List actualResponse = requestBuilder.execute(destination); - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse).hasSize(2); - assertThat(actualResponse).contains(3.14f, 9.81f); - } - - @Test - void testFunctionWithEntityResponse() - { - stubFor( - get(urlPathEqualTo(SERVICE_PATH + '/' + FUNCTION_NAME + "()")) - .willReturn( - okJson("{ \"value\" : [" + "{ \"Name\" : \"Tester1\" }," + "{ \"Name\" : \"Tester2\" }" + "]}"))); - - final CollectionValueFunctionRequestBuilder requestBuilder = - new CollectionValueFunctionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, TestEntity.class); - - final List actualResponse = requestBuilder.execute(destination); - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse).hasSize(2); - assertThat(actualResponse).contains(new TestEntity("Tester1"), new TestEntity("Tester2")); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CsrfTokenOptOutTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CsrfTokenOptOutTest.java deleted file mode 100644 index 85e271ec99..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CsrfTokenOptOutTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static com.github.tomakehurst.wiremock.client.WireMock.absent; -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.patch; -import static com.github.tomakehurst.wiremock.client.WireMock.patchRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; - -/** - * Verifies the backward-compatible CSRF opt-out ({@code withoutCsrfToken()}) and the deprecated no-op - * ({@code withCsrfToken()}) methods on the OData v4 request builders. - */ -@WireMockTest -@SuppressWarnings( "deprecation" ) -class CsrfTokenOptOutTest -{ - private static final String SERVICE_PATH = "/remoteService"; - private static final String ENTITY_URL = SERVICE_PATH + "/People('usr')"; - private static final String PEOPLE_URL = SERVICE_PATH + "/People"; - - private static final String X_CSRF_TOKEN_HEADER_KEY = "x-csrf-token"; - - private DefaultHttpDestination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - } - - @Test - void withoutCsrfTokenSkipsHeadProbeAndSendsNoToken() - { - stubFor(head(anyUrl()).willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, "should-not-be-used"))); - stubFor(patch(urlEqualTo(ENTITY_URL)).willReturn(ok())); - - final Person person = new Person(); - person.setUserName("usr"); - - new UpdateRequestBuilder<>(SERVICE_PATH, person, "People").withoutCsrfToken().execute(destination); - - // no CSRF HEAD probe was fired - verify(0, headRequestedFor(anyUrl())); - // the actual write carries neither a CSRF token nor the internal skip marker - verify(patchRequestedFor(urlEqualTo(ENTITY_URL)).withHeader(X_CSRF_TOKEN_HEADER_KEY, absent())); - } - - @Test - void createWithoutCsrfTokenSkipsHeadProbe() - { - stubFor(head(anyUrl()).willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, "should-not-be-used"))); - stubFor(post(urlPathEqualTo(PEOPLE_URL)).willReturn(ok().withHeader("Content-Type", "application/json"))); - - final Person person = new Person(); - person.setUserName("usr"); - - new CreateRequestBuilder<>(SERVICE_PATH, person, "People").withoutCsrfToken().execute(destination); - - verify(0, headRequestedFor(anyUrl())); - verify(postRequestedFor(urlPathEqualTo(PEOPLE_URL))); - } - - @Test - void batchWithoutCsrfTokenSkipsHeadProbe() - { - stubFor(head(anyUrl()).willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, "should-not-be-used"))); - stubFor(post(urlPathEqualTo(SERVICE_PATH + "/$batch")).willReturn(ok())); - - final Person person = new Person(); - person.setUserName("usr"); - - new BatchRequestBuilder(SERVICE_PATH) - .withoutCsrfToken() - .addChangeset(new CreateRequestBuilder<>(SERVICE_PATH, person, "People")) - .execute(destination); - - verify(0, headRequestedFor(anyUrl())); - verify(postRequestedFor(urlPathEqualTo(SERVICE_PATH + "/$batch"))); - } - - @Test - void actionWithoutCsrfTokenSkipsHeadProbe() - { - final String actionUrl = SERVICE_PATH + "/TestAction"; - stubFor(head(anyUrl()).willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, "should-not-be-used"))); - stubFor(post(urlPathEqualTo(actionUrl)).willReturn(noContent())); - - new SingleValueActionRequestBuilder<>(SERVICE_PATH, "TestAction", Void.class) - .withoutCsrfToken() - .execute(destination); - - verify(0, headRequestedFor(anyUrl())); - verify(postRequestedFor(urlPathEqualTo(actionUrl)).withHeader(X_CSRF_TOKEN_HEADER_KEY, absent())); - } - - @Test - void withCsrfTokenIsNoOpAndReturnsConcreteType() - { - stubFor(head(anyUrl()).willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, "should-not-be-used"))); - - // Compile-time assertion: withCsrfToken() returns the concrete builder type. - final GetAllRequestBuilder builder = - new GetAllRequestBuilder<>(SERVICE_PATH, Person.class, "People").withCsrfToken(); - - // A read request never triggers a CSRF HEAD probe. - stubFor( - get(urlPathEqualTo(PEOPLE_URL)) - .willReturn(ok().withHeader("Content-Type", "application/json").withBody("{\"value\":[]}"))); - - builder.execute(destination); - - verify(0, headRequestedFor(anyUrl())); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CustomFilterExpressionTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CustomFilterExpressionTest.java deleted file mode 100644 index 8d3fa9181a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/CustomFilterExpressionTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ValueBoolean; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableBoolean; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class CustomFilterExpressionTest -{ - @Test - void requestBuilderHasExpectedParameters() - { - final ValueBoolean untypedExpression = FieldReference.of("MiddleName").equalTo("SomeName"); - final FilterableBoolean customFilterExpression = - FilterableBoolean.fromCustomFilter(untypedExpression, Person.class); - - GetAllRequestBuilder requestBuilder = - new DefaultTrippinService().getAllPeople().filter(customFilterExpression); - assertThat(requestBuilder.toRequest().getRelativeUri()).hasParameter("$filter", "(MiddleName eq 'SomeName')"); - } - - @Test - void requestBuilderHasExpectedComplexParameters() - { - final ValueBoolean untypedMultiExpression = - FieldReference - .of("MiddleName") - .equalTo("SomeName") - .and(FieldReference.of("YearsOfExperience").greaterThan(5)); - final FilterableBoolean customFilterExpression = - FilterableBoolean.fromCustomFilter(untypedMultiExpression, Person.class); - - GetAllRequestBuilder requestBuilder = - new DefaultTrippinService().getAllPeople().filter(customFilterExpression); - assertThat(requestBuilder.toRequest().getRelativeUri()) - .hasParameter("$filter", "((MiddleName eq 'SomeName') and (YearsOfExperience gt 5))"); - - } - - @Test - void entityTypeIsCorrect() - { - final ValueBoolean untypedExpression = FieldReference.of("MiddleName").equalTo("SomeName"); - final FilterableBoolean customFilterExpression = - FilterableBoolean.fromCustomFilter(untypedExpression, Person.class); - - assertThat(customFilterExpression.getEntityType()).isEqualTo(Person.class); - } - - @Test - void expressionDelegatedSuccessfully() - { - final ValueBoolean untypedExpression = FieldReference.of("MiddleName").equalTo("SomeName"); - final FilterableBoolean customFilterExpression = - FilterableBoolean.fromCustomFilter(untypedExpression, Person.class); - - assertThat(customFilterExpression.getExpression(ODataProtocol.V4)) - .contains(untypedExpression.getExpression(ODataProtocol.V4)); - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/FieldSerializationTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/FieldSerializationTest.java deleted file mode 100644 index ca571e03ef..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/FieldSerializationTest.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.util.Base64; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; - -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.HttpHeaders; -import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.SneakyThrows; - -class FieldSerializationTest -{ - @Data - @NoArgsConstructor - @AllArgsConstructor - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( GsonVdmAdapterFactory.class ) - @JsonSerialize( using = JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = JacksonVdmObjectDeserializer.class ) - public static class ReferenceObject extends VdmEntity - { - @Getter - private final String odataType = "TestEntity"; - - @Getter - private final String entityCollection = "EntityCollection"; - - @Getter - private final Class type = ReferenceObject.class; - - @ElementName( "ByteValue" ) - @SerializedName( "ByteValue" ) - @JsonProperty( "ByteValue" ) - short byteValue; - - @ElementName( "SByteValue" ) - @SerializedName( "SByteValue" ) - @JsonProperty( "SByteValue" ) - Byte sByteValue; - - @ElementName( "Int16Value" ) - @SerializedName( "Int16Value" ) - @JsonProperty( "Int16Value" ) - short int16Value; - - @ElementName( "Int32Value" ) - @SerializedName( "Int32Value" ) - @JsonProperty( "Int32Value" ) - int int32Value; - - @ElementName( "Int64Value" ) - @SerializedName( "Int64Value" ) - @JsonProperty( "Int64Value" ) - long int64Value; - - @ElementName( "SingleValue" ) - @SerializedName( "SingleValue" ) - @JsonProperty( "SingleValue" ) - float singleValue; - - @ElementName( "DoubleValue" ) - @SerializedName( "DoubleValue" ) - @JsonProperty( "DoubleValue" ) - double doubleValue; - - @ElementName( "DecimalValue" ) - @SerializedName( "DecimalValue" ) - @JsonProperty( "DecimalValue" ) - BigDecimal decimalValue; - - @ElementName( "BooleanValue" ) - @SerializedName( "BooleanValue" ) - @JsonProperty( "BooleanValue" ) - boolean booleanValue; - - @ElementName( "StringValue" ) - @SerializedName( "StringValue" ) - @JsonProperty( "StringValue" ) - String stringValue; - - @ElementName( "BinaryValue" ) - @SerializedName( "BinaryValue" ) - @JsonProperty( "BinaryValue" ) - byte[] binaryValue; - - @ElementName( "GuidValue" ) - @SerializedName( "GuidValue" ) - @JsonProperty( "GuidValue" ) - UUID guidValue; - - @ElementName( "TimeOfDayValue" ) - @SerializedName( "TimeOfDayValue" ) - @JsonProperty( "TimeOfDayValue" ) - LocalTime timeOfDayValue; - - @ElementName( "DateValue" ) - @SerializedName( "DateValue" ) - @JsonProperty( "DateValue" ) - LocalDate dateValue; - - @ElementName( "DateTimeOffsetValue" ) - @SerializedName( "DateTimeOffsetValue" ) - @JsonProperty( "DateTimeOffsetValue" ) - OffsetDateTime dateTimeOffsetValue; - - // https://docs.oasis-open.org/odata/odata-json-format/v4.01/csprd06/odata-json-format-v4.01-csprd06.html#sec_PrimitiveValue - private static final String PAYLOAD_ODATA_REFERENCE = """ - {\ - "@odata.type":"#TestEntity",\ - "ByteValue":255,\ - "SByteValue":-128,\ - "Int16Value":1,\ - "Int32Value":-1234,\ - "Int64Value":1234567890,\ - "SingleValue":1234.5677,\ - "DoubleValue":1234.5678,\ - "DecimalValue":110,\ - "BooleanValue":false,\ - "StringValue":"test",\ - "BinaryValue":"AQID",\ - "GuidValue":"00000000-1111-2222-3333-444444444444",\ - "TimeOfDayValue":"12:00:00",\ - "DateValue":"1999-03-14",\ - "DateTimeOffsetValue":"1999-03-14T00:00:00Z",\ - "GeographyPoint":{"type":"Point","coordinates":[142.1,64.1]}\ - }\ - """; - - private static final String PAYLOAD_ODATA_REFERENCE_BASE64URL = - PAYLOAD_ODATA_REFERENCE.replace("\"BinaryValue\":\"AQID\"", "\"BinaryValue\":\"-__v\""); - - private static final String PAYLOAD_ODATA_REFERENCE_MIXED_BASE64_ALPHABET = - PAYLOAD_ODATA_REFERENCE.replace("\"BinaryValue\":\"AQID\"", "\"BinaryValue\":\"+__v\""); - - static final String Base_64 = "+//v"; - static final String Base_64_Url = "-__v"; - } - - @Test - void testBinaryFieldParsingFromResponsePayload() - { - final ODataRequestResultGeneric result = mockRequestResult(ReferenceObject.PAYLOAD_ODATA_REFERENCE); - final ReferenceObject referenceResult = result.as(ReferenceObject.class); - - Objects.requireNonNull(referenceResult); - assertThat(referenceResult.getBinaryValue()).isEqualTo(new byte[] { 1, 2, 3 }); - - final String ser = - new CreateRequestBuilder<>("/", referenceResult, "EntityCollection").toRequest().getSerializedEntity(); - assertThat(ser).isEqualTo(ReferenceObject.PAYLOAD_ODATA_REFERENCE); - } - - @Test - void testBinaryFieldParsingFromBase64UrlResponsePayload() - { - final ODataRequestResultGeneric result = mockRequestResult(ReferenceObject.PAYLOAD_ODATA_REFERENCE_BASE64URL); - final ReferenceObject referenceResult = result.as(ReferenceObject.class); - - Objects.requireNonNull(referenceResult); - assertThat(referenceResult.getBinaryValue()) - .isEqualTo(Base64.getUrlDecoder().decode(ReferenceObject.Base_64_Url)); - - final String ser = - new CreateRequestBuilder<>("/", referenceResult, "EntityCollection").toRequest().getSerializedEntity(); - assertThat(ser).contains("\"BinaryValue\":\"" + ReferenceObject.Base_64 + "\""); - } - - @Test - void testCustomFieldParsingFromResponsePayload() - { - final ODataRequestResultGeneric result = mockRequestResult(ReferenceObject.PAYLOAD_ODATA_REFERENCE); - - final ReferenceObject referenceResult = result.as(ReferenceObject.class); - - Objects.requireNonNull(referenceResult); - assertThat(referenceResult.getCustomFieldNames()).containsExactly("GeographyPoint"); - assertThat(referenceResult. getCustomField("GeographyPoint")).isInstanceOf(Map.class); - } - - @Test - void testBinaryFieldParsingFromMixedBase64AlphabetNormalized() - { - final ODataRequestResultGeneric result = - mockRequestResult(ReferenceObject.PAYLOAD_ODATA_REFERENCE_MIXED_BASE64_ALPHABET); - final ReferenceObject referenceResult = result.as(ReferenceObject.class); - - Objects.requireNonNull(referenceResult); - assertThat(referenceResult.getBinaryValue()).isEqualTo(Base64.getDecoder().decode(ReferenceObject.Base_64)); - - final String ser = - new CreateRequestBuilder<>("/", referenceResult, "EntityCollection").toRequest().getSerializedEntity(); - assertThat(ser).contains("\"BinaryValue\":\"" + ReferenceObject.Base_64 + "\""); - } - - @SneakyThrows - private static ODataRequestResultGeneric mockRequestResult( final String payload ) - { - final ODataRequestGeneric request = mock(ODataRequestGeneric.class); - when(request.getProtocol()).thenReturn(ODataProtocol.V4); - - final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK"); - response.setEntity(new StringEntity(payload)); - response.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()); - - return new ODataRequestResultGeneric(request, response); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/HeadersHandlingTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/HeadersHandlingTest.java deleted file mode 100644 index 33f57a676e..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/HeadersHandlingTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.patch; -import static com.github.tomakehurst.wiremock.client.WireMock.patchRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.List; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; - -@WireMockTest -class HeadersHandlingTest -{ - private static final WireMockConfiguration WIREMOCK_CONFIGURATION = wireMockConfig().dynamicPort(); - private static final String SERVICE_PATH = "/remoteService"; - private static final String ENTITY_URL = SERVICE_PATH + "/People('usr')"; - - private static final String X_CSRF_TOKEN_HEADER_KEY = "x-csrf-token"; - private static final String X_CSRF_TOKEN_HEADER_VALUE = "awesome-csrf-token"; - private static final String SET_COOKIE_HEADER_KEY = "Set-Cookie"; - - private DefaultHttpDestination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - } - - @Test - void testUpdateRequestContainsHeadersWithNonUniqueKeys() - { - stubFor(head(anyUrl()).willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, X_CSRF_TOKEN_HEADER_VALUE))); - - stubFor( - patch(urlEqualTo(ENTITY_URL)) - .willReturn( - ok() - .withHeader(SET_COOKIE_HEADER_KEY, "SimpleKey=SimpleValue") - .withHeader(SET_COOKIE_HEADER_KEY, "KeyWithoutAValue") - .withHeader(SET_COOKIE_HEADER_KEY, "KeyWithCommaValue=Value, which contains a comma") - .withHeader( - SET_COOKIE_HEADER_KEY, - "MultiValueKey1=Value 1, with comma; MultiValueKey2=Value 2 ; MultiValueKey3;"))); - - final Person person = new Person(); - person.setUserName("usr"); - - final UpdateRequestBuilder builder = new UpdateRequestBuilder<>(SERVICE_PATH, person, "People"); - - final ModificationResponse response = builder.execute(destination); - - final List responseHeaders = - ImmutableList.copyOf(response.getResponseHeaders().get(SET_COOKIE_HEADER_KEY)); - - assertThat(responseHeaders) - .containsExactly( - "SimpleKey=SimpleValue", - "KeyWithoutAValue", - "KeyWithCommaValue=Value, which contains a comma", - "MultiValueKey1=Value 1, with comma; MultiValueKey2=Value 2 ; MultiValueKey3;"); - } - - @Test - void testMultipleHeadersWithSameKey() - { - final Person person = new Person(); - person.setUserName("usr"); - - // patch request with 2 cookie headers with the same key - final UpdateRequestBuilder builder = - new UpdateRequestBuilder<>(SERVICE_PATH, person, "People") - .withHeader(SET_COOKIE_HEADER_KEY, "foo") - .withHeader(SET_COOKIE_HEADER_KEY, "bar"); - - executeMultipleHeadersWithSameKey(builder); - } - - @Test - void testMultipleHeadersWithSameKeyAtOnce() - { - final Person person = new Person(); - person.setUserName("usr"); - - // patch request with 2 cookie headers with the same key - final UpdateRequestBuilder builder = - new UpdateRequestBuilder<>(SERVICE_PATH, person, "People") - .withHeaders(ImmutableMap.of(SET_COOKIE_HEADER_KEY, "foo")) - .withHeaders(ImmutableMap.of(SET_COOKIE_HEADER_KEY, "bar")); - - executeMultipleHeadersWithSameKey(builder); - } - - public void executeMultipleHeadersWithSameKey( UpdateRequestBuilder builder ) - { - // With Apache HttpClient 5 the CSRF token HEAD request is issued by the client-level - // CsrfTokenInterceptor, which does not propagate the request's custom headers. The custom - // headers are only sent on the actual (PATCH) request, so that is where we assert them. - stubFor(head(anyUrl()).willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, X_CSRF_TOKEN_HEADER_VALUE))); - - // the server will check for the cookie headers when receiving the patch request - stubFor( - patch(urlEqualTo(ENTITY_URL)) - .withHeader(SET_COOKIE_HEADER_KEY, equalTo("foo")) - .withHeader(SET_COOKIE_HEADER_KEY, equalTo("bar")) - .willReturn(ok())); - - builder.execute(destination); - - // check that both header values with the same key are sent on the request - verify( - patchRequestedFor(urlEqualTo(ENTITY_URL)) - .withHeader(SET_COOKIE_HEADER_KEY, equalTo("foo")) - .withHeader(SET_COOKIE_HEADER_KEY, equalTo("bar"))); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/HttpResponseEvaluationTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/HttpResponseEvaluationTest.java deleted file mode 100644 index 1c52b487c1..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/HttpResponseEvaluationTest.java +++ /dev/null @@ -1,204 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import static org.apache.hc.core5.http.ContentType.APPLICATION_JSON; -import static org.apache.hc.core5.http.ContentType.TEXT_PLAIN; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; -import java.util.Map; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.HttpStatus; -import org.apache.hc.core5.http.io.entity.InputStreamEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; - -import lombok.SneakyThrows; - -public class HttpResponseEvaluationTest -{ - private static final Destination DESTINATION = DefaultHttpDestination.builder("foo").build(); - - private HttpClient httpClient; - private BasicClassicHttpResponse httpResponse; - private InputStreamEntity httpEntity; - private InputStream inputStream; - - @SneakyThrows - void mockHttpResponse( final ContentType contentType, final String payload ) - { - httpClient = mock(HttpClient.class); - inputStream = spy(new ByteArrayInputStream(payload.getBytes(UTF_8))); - httpEntity = spy(new InputStreamEntity(inputStream, contentType)); - httpResponse = spy(new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK")); - httpResponse.setEntity(httpEntity); - ODataApacheHttpClient5Accessor.setHttpClientFactory(destination -> httpClient); - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(httpResponse); - } - - @AfterEach - void teardown() - { - ODataApacheHttpClient5Accessor.setHttpClientFactory(null); - ODataApacheHttpClient5Accessor.setHttpClientCache(null); - } - - @SneakyThrows - @Test - void testCreate() - { - mockHttpResponse(APPLICATION_JSON, "{}"); - - final ModificationResponse result = - new CreateRequestBuilder<>("/path", new TestEntity(), "TestEntitySet").execute(DESTINATION); - - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - - assertThat(result.getResponseStatusCode()).isEqualTo(200); - } - - @SneakyThrows - @Test - void testUpdate() - { - mockHttpResponse(APPLICATION_JSON, "{}"); - - final TestEntity testEntity = new TestEntity(); - testEntity.setId("id"); - - final ModificationResponse result = - new UpdateRequestBuilder<>("/path", testEntity, "TestEntitySet").execute(DESTINATION); - - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - - assertThat(result.getResponseStatusCode()).isEqualTo(200); - } - - @SneakyThrows - @Test - void testDelete() - { - mockHttpResponse(APPLICATION_JSON, "{}"); - - final ModificationResponse result = - new DeleteRequestBuilder<>("/path", new TestEntity(), "TestEntitySet").execute(DESTINATION); - - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - - assertThat(result.getResponseStatusCode()).isEqualTo(200); - } - - @SneakyThrows - @Test - void testReadAll() - { - mockHttpResponse(APPLICATION_JSON, "{\"value\": []}"); - - final List result = - new GetAllRequestBuilder<>("/path", TestEntity.class, "TestEntitySet").execute(DESTINATION); - - assertThat(result).isNotNull(); - - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - } - - @SneakyThrows - @Test - void testReadByKey() - { - mockHttpResponse(APPLICATION_JSON, "{}"); - - final TestEntity result = - new GetByKeyRequestBuilder<>("/path", TestEntity.class, Map.of("id", "foo"), "TestEntitySet") - .execute(DESTINATION); - - assertThat(result).isNotNull(); - - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - } - - @SneakyThrows - @Test - void testReadCount() - { - mockHttpResponse(TEXT_PLAIN, "42"); - - final Long result = new CountRequestBuilder<>("/path", TestEntity.class, "TestEntitySet").execute(DESTINATION); - - assertThat(result).isNotNull(); - - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - } - - @SneakyThrows - @Test - void testAction() - { - mockHttpResponse(APPLICATION_JSON, "{}"); - - final ActionResponseSingle result = - new SingleValueActionRequestBuilder<>("/path", "ActionName", TestEntity.class).execute(DESTINATION); - - assertThat(result).isNotNull(); - - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - } - - @SneakyThrows - @Test - void testFunction() - { - mockHttpResponse(APPLICATION_JSON, "{\"value\":[]}"); - - final List result = - new CollectionValueFunctionRequestBuilder<>("/path", "FunctionName", TestEntity.class).execute(DESTINATION); - - assertThat(result).isNotNull(); - - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - verify(httpResponse, times(1)).getEntity(); - verify(httpEntity, times(1)).writeTo(any(OutputStream.class)); - verify(inputStream, times(2)).close(); - } - - // count function action -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ModificationResponseTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ModificationResponseTest.java deleted file mode 100644 index d6d26218c7..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ModificationResponseTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static java.nio.charset.StandardCharsets.UTF_8; - -import static com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol.V4; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpStatus; -import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.apache.hc.core5.http.message.BasicHeader; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestUpdate; -import com.sap.cloud.sdk.datamodel.odata.client.request.UpdateStrategy; -import com.sap.cloud.sdk.result.ElementName; - -import io.vavr.control.Option; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.SneakyThrows; -import lombok.ToString; - -class ModificationResponseTest -{ - @Data - @NoArgsConstructor - @AllArgsConstructor - @ToString( doNotUseGetters = true, callSuper = true ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - @JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) - public static class TestObject extends VdmEntity - { - @Getter - private final String odataType = "TestObject"; - - @Getter - private final Class type = TestObject.class; - - @ElementName( "foo" ) - private String name; - - @Nonnull - @Override - protected String getEntityCollection() - { - return odataType; - } - } - - @Test - void testEntityResponse() - { - final TestObject inputObject = new TestObject(); - - final ODataRequestGeneric request = mock(ODataRequestGeneric.class); - when(request.getProtocol()).thenReturn(V4); - - final Header[] responseHeaders = { new BasicHeader("fizz", "buzz"), new BasicHeader("fizz", "fuzz, bizz=1") }; - - final ClassicHttpResponse response = mock(ClassicHttpResponse.class); - doReturn(responseHeaders).when(response).getHeaders(); - doReturn(responseHeaders).when(response).getHeaders("ETag"); - doReturn(new StringEntity("{\"foo\":\"bar\"}", UTF_8)).when(response).getEntity(); - doReturn(HttpStatus.SC_OK).when(response).getCode(); - - final ODataRequestResultGeneric result = new ODataRequestResultGeneric(request, response); - final ModificationResponse modification = ModificationResponse.of(result, inputObject); - - assertThat(modification).isNotNull(); - assertThat(modification.getResponseStatusCode()).isEqualTo(HttpStatus.SC_OK); - assertThat(modification.getRequestEntity()).isSameAs(inputObject); - - assertThat(modification.getResponseEntity().get()).isNotSameAs(inputObject); - assertThat(modification.getResponseEntity().get()).isEqualTo(new TestObject("bar")); - assertThat(modification.getModifiedEntity()).isEqualTo(new TestObject("bar")); - - assertThat(modification.getResponseHeaders()).containsOnlyKeys("fizz"); - assertThat(modification.getResponseHeaders().get("fizz")).containsExactly("buzz", "fuzz, bizz=1"); - } - - @Test - void testEmptyResponse() - { - final TestObject inputObject = new TestObject(); - - final ODataRequestGeneric request = mock(ODataRequestGeneric.class); - when(request.getProtocol()).thenReturn(V4); - - final ClassicHttpResponse response = mock(ClassicHttpResponse.class); - doReturn(new Header[0]).when(response).getHeaders(); - doReturn(new Header[0]).when(response).getHeaders("ETag"); - doReturn(new StringEntity("", UTF_8)).when(response).getEntity(); - doReturn(HttpStatus.SC_NO_CONTENT).when(response).getCode(); - - final ODataRequestResultGeneric result = new ODataRequestResultGeneric(request, response); - final ModificationResponse modification = ModificationResponse.of(result, inputObject); - - assertThat(modification).isNotNull(); - assertThat(modification.getResponseStatusCode()).isEqualTo(HttpStatus.SC_NO_CONTENT); - assertThat(modification.getRequestEntity()).isSameAs(inputObject); - assertThat(modification.getModifiedEntity()).isNotSameAs(inputObject); - assertThat(modification.getModifiedEntity()).isEqualTo(inputObject); - assertThat(modification.getResponseHeaders()).isEmpty(); - } - - @SneakyThrows - @Test - void testResponseIsOnlyEvaluatedOnce() - { - final TestObject inputObject = new TestObject(); - - final ClassicHttpResponse response = spy(new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK")); - response.setHeaders(new Header[0]); - response.setEntity(new StringEntity("{\"foo\":\"bar\"}", UTF_8)); - - final ODataEntityKey key = ODataEntityKey.of(Map.of("id", 42), V4); - final ODataRequestUpdate request = - new ODataRequestUpdate("service/path", "EntitySet", key, "{}", UpdateStrategy.REPLACE_WITH_PUT, null, V4); - - final HttpClient httpClient = mock(HttpClient.class); - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response); - - final ODataRequestResultGeneric result = request.execute(httpClient); - final ModificationResponse modification = ModificationResponse.of(result, inputObject); - - modification.getResponseEntity(); - final Option responseEntity = modification.getResponseEntity(); - assertThat(responseEntity).isNotNull(); - - modification.getModifiedEntity(); - final TestObject modifiedEntity = modification.getModifiedEntity(); - assertThat(modifiedEntity).isNotNull(); - - verify(response, times(1)).getEntity(); - verify(httpClient, times(1)).executeOpen(isNull(), any(), isNull()); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/NestedEntityTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/NestedEntityTest.java deleted file mode 100644 index 8edbdbb9a3..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/NestedEntityTest.java +++ /dev/null @@ -1,413 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static com.github.tomakehurst.wiremock.client.WireMock.delete; -import static com.github.tomakehurst.wiremock.client.WireMock.deleteRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.patch; -import static com.github.tomakehurst.wiremock.client.WireMock.patchRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; -import static org.assertj.core.api.Assertions.assertThatNoException; - -import javax.annotation.Nonnull; - -import org.apache.hc.core5.http.HttpHeaders; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCount; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCreate; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestDelete; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestReadByKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestUpdate; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.PlanItem; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Trip; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -@WireMockTest -class NestedEntityTest -{ - private static final WireMockConfiguration WIREMOCK_CONFIGURATION = wireMockConfig().dynamicPort(); - - private DefaultHttpDestination destination; - private DefaultTrippinService service; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - service = new DefaultTrippinService().withServicePath("/TripPinServiceRW"); - } - - @Test - void testGetSingleNestedFriend() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - final ODataRequestReadByKey getAll = - service.forEntity(personByKey).navigateTo(Person.TO_BEST_FRIEND).get().toRequest(); - - assertThat(getAll.getRelativeUri()).hasToString("/TripPinServiceRW/People('russellwhyte')/BestFriend"); - } - - @Test - void testGetSingleNestedNestedNestedFriend() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - final ODataRequestReadByKey getAll = - service - .forEntity(personByKey) - .navigateTo(Person.TO_BEST_FRIEND) - .navigateTo(Person.TO_BEST_FRIEND) - .navigateTo(Person.TO_BEST_FRIEND) - .get() - .toRequest(); - - assertThat(getAll.getRelativeUri()) - .hasToString("/TripPinServiceRW/People('russellwhyte')/BestFriend/BestFriend/BestFriend"); - } - - @Test - void testDeleteSingleNestedFriend() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - final ODataRequestDelete delete = - service.forEntity(personByKey).navigateTo(Person.TO_BEST_FRIEND).delete().toRequest(); - - assertThat(delete.getRelativeUri()).hasToString("/TripPinServiceRW/People('russellwhyte')/BestFriend"); - } - - @Test - void testUpdateSingleNestedFriend() - { - final Person friend = new Person(); - friend.setLastName("Jobs"); - - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - final ODataRequestUpdate update = - service.forEntity(personByKey).navigateTo(Person.TO_BEST_FRIEND).update(friend).toRequest(); - - assertThat(update.getRelativeUri()).hasToString("/TripPinServiceRW/People('russellwhyte')/BestFriend"); - } - - @Test - void testCreateNestedTrip() - { - final Trip customTrip = new Trip(); - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - final ODataRequestCreate create = - service.forEntity(personByKey).navigateTo(Person.TO_TRIPS).create(customTrip).toRequest(); - - assertThat(create.getRelativeUri()).hasToString("/TripPinServiceRW/People('russellwhyte')/Trips"); - } - - @Test - void testGetAllNestedTrips() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - final ODataRequestRead getAll = service.forEntity(personByKey).navigateTo(Person.TO_TRIPS).getAll().toRequest(); - - assertThat(getAll.getRelativeUri()).hasToString("/TripPinServiceRW/People('russellwhyte')/Trips"); - } - - @Test - void testDeleteBestFriend() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - final ODataRequestDelete delete = - service - .forEntity(personByKey) - .navigateTo(Person.TO_BEST_FRIEND) - .delete() - .matchAnyVersionIdentifier() - .toRequest(); - - assertThat(delete.getRelativeUri()).hasToString("/TripPinServiceRW/People('russellwhyte')/BestFriend"); - } - - @Test - void testUpdateBestFriend() - { - final Person oldPerson = Person.builder().userName("oldMan").build(); - final Person newPerson = Person.builder().userName("youngMan").build(); - - final ODataRequestUpdate update = - service - .forEntity(oldPerson) - .navigateTo(Person.TO_BEST_FRIEND) - .update(newPerson) - .matchAnyVersionIdentifier() - .toRequest(); - - assertThat(update.getRelativeUri()).hasToString("/TripPinServiceRW/People('oldMan')/BestFriend"); - } - - @Test - void testCountNestedTrip() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - final ODataRequestCount count = service.forEntity(personByKey).navigateTo(Person.TO_TRIPS).count().toRequest(); - - assertThat(count.getRelativeUri()).hasToString("/TripPinServiceRW/People('russellwhyte')/Trips/$count"); - } - - @Test - void testCountNestedTripToRequestIsStable() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - final CountRequestBuilder countBuilder = - service.forEntity(personByKey).navigateTo(Person.TO_TRIPS).count(); - - assertThat(countBuilder.toRequest().getRelativeUri()) - .hasToString("/TripPinServiceRW/People('russellwhyte')/Trips/$count"); - assertThat(countBuilder.toRequest().getRelativeUri()) - .hasToString("/TripPinServiceRW/People('russellwhyte')/Trips/$count"); - } - - @Test - void testGetByKeyNestedPlanItem() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - final Trip tripByKey = Trip.builder().tripId(1003).build(); - - final GetAllRequestBuilder builder = - service - .forEntity(personByKey) - .navigateTo(Person.TO_TRIPS) - .forEntity(tripByKey) - .navigateTo(Trip.TO_PLAN_ITEMS) - .getAll(); - - final ODataRequestRead getAll = builder.toRequest(); - - assertThat(getAll.getRelativeUri()) - .hasToString("/TripPinServiceRW/People('russellwhyte')/Trips(1003)/PlanItems"); - } - - @Test - void testCreateNestedPlanItem() - { - final PlanItem customPlanItem = new PlanItem(); - final Person personByKey = Person.builder().userName("russellwhyte").build(); - final Trip tripByKey = Trip.builder().tripId(1003).build(); - - final CreateRequestBuilder createdPlanItem = - service - .forEntity(personByKey) - .navigateTo(Person.TO_TRIPS) - .forEntity(tripByKey) - .navigateTo(Trip.TO_PLAN_ITEMS) - .create(customPlanItem) - .withHeader("header", "value") - .withQueryParameter("query", "parameter"); - - // check client - final ODataRequestCreate requestCreate = createdPlanItem.toRequest(); - assertThat(requestCreate.getRelativeUri()).hasQuery("query=parameter"); - assertThat(requestCreate.getRelativeUri()) - .hasPath("/TripPinServiceRW/People('russellwhyte')/Trips(1003)/PlanItems"); - - // check VDM execution - final String headPath = "/TripPinServiceRW"; - final String postPath = "/TripPinServiceRW/People('russellwhyte')/Trips(1003)/PlanItems"; - - stubFor(head(urlPathEqualTo(headPath)).willReturn(ok().withHeader("x-csrf-token", "foo"))); - stubFor( - post(urlPathEqualTo(postPath)) - .withQueryParam("query", equalTo("parameter")) - .withHeader("header", equalTo("value")) - .willReturn(okJson("{}"))); - - final ModificationResponse createResponse = createdPlanItem.execute(destination); - assertThat(createResponse).isNotNull(); - assertThat(createResponse.getResponseStatusCode()).isEqualTo(200); - verify(1, postRequestedFor(urlPathEqualTo(postPath))); - } - - @Test - void testDeleteEntityWithEtag() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - personByKey.setVersionIdentifier("foobar"); - - final String headPath = service.getServicePath(); - final String deletePath = headPath + "/People('russellwhyte')"; - - stubFor(head(urlPathEqualTo(headPath)).willReturn(ok().withHeader("x-csrf-token", "foo"))); - stubFor(delete(urlPathEqualTo(deletePath)).willReturn(okJson("{}"))); - - final DeleteRequestBuilder delete = service.forEntity(personByKey).delete(); - - assertThat(delete.toRequest().getRelativeUri()).hasToString(deletePath); - - delete.execute(destination); - - verify(deleteRequestedFor(urlEqualTo(deletePath)).withHeader(HttpHeaders.IF_MATCH, equalTo("foobar"))); - } - - @Test - void testUpdateEntityWithEtag() - { - final Person personByKey = Person.builder().userName("russellwhyte").build(); - - personByKey.setVersionIdentifier("foobar"); - - final String headPath = service.getServicePath(); - final String updatePath = headPath + "/People('russellwhyte')"; - - stubFor(head(urlPathEqualTo(headPath)).willReturn(ok().withHeader("x-csrf-token", "foo"))); - stubFor(patch(urlPathEqualTo(updatePath)).willReturn(okJson("{}"))); - - final UpdateRequestBuilder update = service.forEntity(personByKey).update(personByKey); - - assertThat(update.toRequest().getRelativeUri()).hasToString(updatePath); - - update.execute(destination); - - verify(patchRequestedFor(urlEqualTo(updatePath)).withHeader(HttpHeaders.IF_MATCH, equalTo("foobar"))); - } - - @Test - void testTypeRestrictionForVdmEntitySet() - { - final EntityWithoutEntitySet entityWithoutEntitySet = new EntityWithoutEntitySet(); - final EntityWithEntitySet entityWithEntitySet = new EntityWithEntitySet(); - - assertThatIllegalStateException().isThrownBy(() -> service.forEntity(entityWithoutEntitySet)); - assertThatNoException().isThrownBy(() -> service.forEntity(entityWithEntitySet)); - - assertThatNoException().isThrownBy(() -> { - service - .forEntity(entityWithEntitySet) - .navigateTo(EntityWithEntitySet.NAVIGATION_PROPERTY) - .forEntity(entityWithoutEntitySet); - }); - } - - private static class EntityWithoutEntitySet extends VdmEntity - { - @Nonnull - @Override - protected String getEntityCollection() - { - return "outside-of-entity-set"; - } - - @Nonnull - @Override - public String getOdataType() - { - return getEntityCollection(); - } - - @Nonnull - @Override - public Class getType() - { - return EntityWithoutEntitySet.class; - } - } - - private static class EntityWithEntitySet extends VdmEntity implements VdmEntitySet - { - public static final NavigationProperty.Collection NAVIGATION_PROPERTY = - new NavigationProperty.Collection<>(EntityWithEntitySet.class, "Navigations", EntityWithoutEntitySet.class); - - @Nonnull - @Override - protected String getEntityCollection() - { - return "outside-of-entity-set"; - } - - @Nonnull - @Override - public String getOdataType() - { - return getEntityCollection(); - } - - @Nonnull - @Override - public Class getType() - { - return EntityWithEntitySet.class; - } - } - - /* Commented out since otherwise this leads to a traceability mapping failure - @Disabled( "Use this to run and check against the reference OData service." ) - @Test - void integrationTestCreateNestedEntity() - throws IOException - { - final HttpDestination destination = TripPinUtility.getDestinationRW(); - - final String RANDOM_CONFIRMATION_CODE = "" + new Random().nextLong(); - - final PlanItem somePlanItem = - PlanItem - .builder() - .confirmationCode(RANDOM_CONFIRMATION_CODE) - .duration(Duration.ofHours(3)) - .endsAt(LocalDate.of(2014, Month.JUNE, 1).atStartOfDay().atOffset(ZoneOffset.UTC)) - .startsAt(LocalDate.of(2014, Month.MAY, 1).atStartOfDay().atOffset(ZoneOffset.UTC)) - .build(); - - final Person personByKey = Person.builder().userName("russellwhyte").build(); - final Trip tripByKey = Trip.builder().tripId(1003L).build(); - - // Create new item - final ModificationResponse createResult = - service - .forEntity(personByKey) - .navigateTo(Person.TO_TRIPS) - .forEntity(tripByKey) - .navigateTo(Trip.PLAN_ITEMS) - .create(somePlanItem) - .execute(destination); - - // Assert on positive feedback - assertThat(createResult).isNotNull(); - - // Query newly created item - final Person personFound = - service - .getPersonsByKey("russellwhyte") - .select( - Person.TO_TRIPS.filter(Trip.TRIP_ID.equalTo(1003L)).select( - Trip.PLAN_ITEMS.select(PlanItem.CONFIRMATION_CODE))) - .execute(destination); - - // Find item in response - assertThat(personFound).isNotNull(); - assertThat(personFound.getTrips()).isNotEmpty().anySatisfy( - trip -> assertThat(trip.getPlanItems()) - .anySatisfy(item -> assertThat(item.getConfirmationCode()).isEqualTo(RANDOM_CONFIRMATION_CODE))); - } - */ -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ODataRequestImplTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ODataRequestImplTest.java deleted file mode 100644 index ae3b3b20e8..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ODataRequestImplTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Trip; - -class ODataRequestImplTest -{ - @Test - void testRequestWithoutNestedRequests() - { - final NavigationPropertyCollectionQuery request = - NavigationPropertyCollectionQuery.ofSubQuery("ASDF"); - - request - .select(Person.FIRST_NAME, Person.LAST_NAME) - .filter(Person.FIRST_NAME.contains("foo")) - .top(5) - .skip(3) - .orderBy(Person.USER_NAME.desc()); - - final String expected = - "$select=FirstName,LastName;$filter=contains(FirstName,'foo');$top=5;$skip=3;$orderby=UserName%20desc"; - final String unencodedExpected = - "$select=FirstName,LastName;$filter=contains(FirstName,'foo');$top=5;$skip=3;$orderby=UserName desc"; - - assertThat(request.getEncodedQueryString()).isEqualTo(expected); - assertThat(request.getQueryString()).isEqualTo(unencodedExpected); - } - - @Test - void testRequestWithNestedRequests() - { - final NavigationPropertyCollectionQuery request = - NavigationPropertyCollectionQuery.ofSubQuery("ASDF"); - - request - .select(Person.FIRST_NAME, Person.LAST_NAME) - .select(Person.TO_BEST_FRIEND.select(Person.TO_TRIPS.select(Trip.DESCRIPTION).top(10))) - .filter(Person.FIRST_NAME.contains("foo")); - - final String expected = - "$select=FirstName,LastName;$expand=BestFriend($expand=Trips($select=Description;$top=10));$filter=contains(FirstName,'foo')"; - final String unencodedExpected = - "$select=FirstName,LastName;$expand=BestFriend($expand=Trips($select=Description;$top=10));$filter=contains(FirstName,'foo')"; - - assertThat(request.getEncodedQueryString()).isEqualTo(expected); - assertThat(request.getQueryString()).isEqualTo(unencodedExpected); - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ODataV4BatchRequestUnitTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ODataV4BatchRequestUnitTest.java deleted file mode 100644 index 3b40217593..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ODataV4BatchRequestUnitTest.java +++ /dev/null @@ -1,302 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static com.sap.cloud.sdk.datamodel.odatav4.TestUtility.readResourceFileCrlf; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.when; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.client5.http.classic.methods.HttpPost; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.io.entity.InputStreamEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5FactoryBuilder; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultMultipartGeneric; - -import lombok.SneakyThrows; - -@WireMockTest -class ODataV4BatchRequestUnitTest -{ - private static final TestEntityService SERVICE = new TestEntityService() - { - }; - - private static final WireMockConfiguration WIREMOCK_CONFIGURATION = wireMockConfig().dynamicPort(); - private static final String X_CSRF_TOKEN_HEADER_KEY = "x-csrf-token"; - private static final String X_CSRF_TOKEN_HEADER_FETCH_VALUE = "fetch"; - private static final String X_CSRF_TOKEN_HEADER_VALUE = "awesome-csrf-token"; - private static final String REQUEST_URL_BATCH = "/$batch"; - - private static final String REQUEST_BODY = - readResourceFileCrlf(ODataV4BatchRequestUnitTest.class, "BatchReadsAndWritesSuccessRequest.txt"); - private static final String RESPONSE_BODY = - readResourceFileCrlf(ODataV4BatchRequestUnitTest.class, "BatchReadsAndWritesSuccessResponse.txt"); - - private static final TestEntity ENTITY_CREATE = new TestEntity(); - private static final TestEntity ENTITY_UPDATE = TestEntity.builder().id("upd").build(); - private static final TestEntity ENTITY_DELETE = TestEntity.builder().id("del").build(); - - private static final GetAllRequestBuilder READ_ALL = SERVICE.getTestEntities(); - private static final GetByKeyRequestBuilder READ_BY_KEY = SERVICE.getTestEntitiesByKey("foobar"); - private static final CreateRequestBuilder CREATE = SERVICE.createTestEntity(ENTITY_CREATE); - private static final UpdateRequestBuilder UPDATE = SERVICE.updateTestEntity(ENTITY_UPDATE); - private static final DeleteRequestBuilder DELETE = SERVICE.deleteTestEntity(ENTITY_DELETE); - private static final SingleValueFunctionRequestBuilder FUNC_SINGLE = SERVICE.functionSingleResult(); - private static final CollectionValueFunctionRequestBuilder FUNC_MULTIPLE = SERVICE.functionMultipleResult(); - private static final SingleValueActionRequestBuilder ACT_SINGLE = SERVICE.actionSingleResult(); - private static final CollectionValueActionRequestBuilder ACT_MULTIPLE = SERVICE.actionMultipleResult(); - private static final int MAX_PARALLEL_CONNECTIONS = 10; - - private Destination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - // Mock CSRF token handling - - stubFor( - head(urlEqualTo("/")) - .withHeader(X_CSRF_TOKEN_HEADER_KEY, equalTo(X_CSRF_TOKEN_HEADER_FETCH_VALUE)) - .willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, X_CSRF_TOKEN_HEADER_VALUE))); - - // Mock OData Batch response - final String contentType = "multipart/mixed; boundary=batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef"; - stubFor(post(urlEqualTo(REQUEST_URL_BATCH)).willReturn(okForContentType(contentType, RESPONSE_BODY))); - - ODataApacheHttpClient5Accessor - .setHttpClientFactory( - new ApacheHttpClient5FactoryBuilder() - .withCsrfTokenInterceptor() - .maxConnectionsTotal(MAX_PARALLEL_CONNECTIONS) - .maxConnectionsPerRoute(MAX_PARALLEL_CONNECTIONS) - .build()); - } - - @AfterEach - void teardown() - { - ODataApacheHttpClient5Accessor.setHttpClientFactory(null); - } - - @Test - void testAllOperations() - { - for( int i = 0; i < MAX_PARALLEL_CONNECTIONS * 2; i++ ) { - // execute batched requests - final BatchResponse response = - SERVICE - .batch() - .addReadOperations(READ_ALL, READ_BY_KEY) - .addChangeset(CREATE, UPDATE, DELETE) - .addReadOperations(FUNC_SINGLE, FUNC_MULTIPLE) - .addChangeset(ACT_SINGLE, ACT_MULTIPLE) - .execute(destination); - - // assertion on response parsing - final List readAllResult = response.getReadResult(READ_ALL); - assertThat(readAllResult).isNotEmpty(); - - final TestEntity readByKeyResult = response.getReadResult(READ_BY_KEY); - assertThat(readByKeyResult).isNotNull(); - - final ModificationResponse createResult = response.getModificationResult(CREATE); - assertThat(createResult).isNotNull(); - assertThat(createResult.getResponseEntity()).isNotNull().isNotEqualTo(ENTITY_CREATE); - assertThat(createResult.getResponseHeaders().get("Location")) - .containsExactly("https://localhost/service/TestEntities('new')"); - - final ModificationResponse updateResult = response.getModificationResult(UPDATE); - assertThat(updateResult).isNotNull(); - assertThat(updateResult.getResponseEntity()).isNotNull().isNotEqualTo(ENTITY_CREATE); - - final ModificationResponse deleteResult = response.getModificationResult(DELETE); - assertThat(readByKeyResult).isNotNull(); - assertThat(deleteResult.getResponseEntity()).isEmpty(); - - final Integer functionSingleResult = response.getReadResult(FUNC_SINGLE); - assertThat(functionSingleResult).isNotNull(); - - final List functionMultipleResult = response.getReadResult(FUNC_MULTIPLE); - assertThat(functionMultipleResult).isNotEmpty(); - - final ActionResponseSingle actionSingleResult = response.getModificationResult(ACT_SINGLE); - assertThat(actionSingleResult).isNotNull(); - assertThat(actionSingleResult.getResponseResult()).isNotEmpty(); - - final ActionResponseCollection actionMultipleResult = response.getModificationResult(ACT_MULTIPLE); - assertThat(actionMultipleResult).isNotNull(); - assertThat(actionMultipleResult.getResponseResult()).isNotEmpty(); - assertThat(actionMultipleResult.getResponseResult().get()).isNotEmpty(); - - } - // Verify request body - final String requestContentType = "multipart/mixed;boundary=batch_00000000-0000-0000-0000-000000000001"; - verify( - postRequestedFor(urlEqualTo(REQUEST_URL_BATCH)) - .withHeader("Content-Type", equalTo(requestContentType)) - .withHeader(X_CSRF_TOKEN_HEADER_KEY, equalTo(X_CSRF_TOKEN_HEADER_VALUE)) - .withRequestBody(equalTo(REQUEST_BODY))); - - verify( - MAX_PARALLEL_CONNECTIONS * 2, - headRequestedFor(urlEqualTo("/")) - .withHeader(X_CSRF_TOKEN_HEADER_KEY, equalTo(X_CSRF_TOKEN_HEADER_FETCH_VALUE))); - } - - @Test - void testIdenticalOperations() - { - final CreateRequestBuilder identicalCreate = SERVICE.createTestEntity(ENTITY_CREATE); - - final BatchResponse response = - SERVICE - .batch() - .addReadOperations(READ_ALL, READ_BY_KEY) - .addChangeset(CREATE, identicalCreate) - .execute(destination); - - final ModificationResponse result1 = response.getModificationResult(CREATE); - final ModificationResponse result2 = response.getModificationResult(identicalCreate); - - assertThat(result1.getModifiedEntity().getId()).isEqualTo("new"); - assertThat(result2.getModifiedEntity().getId()).isEqualTo("updated"); - - verify( - 1, - headRequestedFor(urlEqualTo("/")) - .withHeader(X_CSRF_TOKEN_HEADER_KEY, equalTo(X_CSRF_TOKEN_HEADER_FETCH_VALUE))); - } - - @Test - void testLowLevelToHighLevel() - { - final HttpClient httpClient = ApacheHttpClient5Accessor.getHttpClient(destination); - - final BatchRequestBuilder batchBuilder = - SERVICE - .batch() - .addReadOperations(READ_ALL, READ_BY_KEY) - .addChangeset(CREATE, UPDATE, DELETE) - .addReadOperations(FUNC_SINGLE, FUNC_MULTIPLE) - .addChangeset(ACT_SINGLE, ACT_MULTIPLE); - - final ODataRequestBatch lowLevelRequest = batchBuilder.toRequest(); - - final ODataRequestResultMultipartGeneric lowLevelResult = lowLevelRequest.execute(httpClient); - - //Consumer transition from generic result object to typed result - final BatchResponse convertedHighLevelResult = BatchResponse.of(lowLevelResult, batchBuilder); - final BatchResponse expectedHighLevelResult = batchBuilder.execute(destination); - - assertThat(expectedHighLevelResult.getReadResult(READ_ALL)) - .isEqualTo(convertedHighLevelResult.getReadResult(READ_ALL)); - assertThat(expectedHighLevelResult.getReadResult(READ_BY_KEY)) - .isEqualTo(convertedHighLevelResult.getReadResult(READ_BY_KEY)); - assertThat(expectedHighLevelResult.getModificationResult(CREATE).getModifiedEntity()) - .isEqualTo(convertedHighLevelResult.getModificationResult(CREATE).getModifiedEntity()); - assertThat(expectedHighLevelResult.getModificationResult(CREATE).getResponseHeaders()) - .isEqualTo(convertedHighLevelResult.getModificationResult(CREATE).getResponseHeaders()); - assertThat(expectedHighLevelResult.getModificationResult(UPDATE).getModifiedEntity()) - .isEqualTo(convertedHighLevelResult.getModificationResult(UPDATE).getModifiedEntity()); - assertThat(expectedHighLevelResult.getModificationResult(UPDATE).getResponseHeaders()) - .isEqualTo(convertedHighLevelResult.getModificationResult(UPDATE).getResponseHeaders()); - assertThat(expectedHighLevelResult.getModificationResult(DELETE).getModifiedEntity()) - .isEqualTo(convertedHighLevelResult.getModificationResult(DELETE).getModifiedEntity()); - assertThat(expectedHighLevelResult.getModificationResult(DELETE).getResponseHeaders()) - .isEqualTo(convertedHighLevelResult.getModificationResult(DELETE).getResponseHeaders()); - assertThat(expectedHighLevelResult.getReadResult(FUNC_SINGLE)) - .isEqualTo(convertedHighLevelResult.getReadResult(FUNC_SINGLE)); - assertThat(expectedHighLevelResult.getReadResult(FUNC_MULTIPLE)) - .isEqualTo(convertedHighLevelResult.getReadResult(FUNC_MULTIPLE)); - assertThat(expectedHighLevelResult.getModificationResult(ACT_SINGLE).getResponseResult()) - .isEqualTo(convertedHighLevelResult.getModificationResult(ACT_SINGLE).getResponseResult()); - assertThat(expectedHighLevelResult.getModificationResult(ACT_SINGLE).getResponseHeaders()) - .isEqualTo(convertedHighLevelResult.getModificationResult(ACT_SINGLE).getResponseHeaders()); - assertThat(expectedHighLevelResult.getModificationResult(ACT_MULTIPLE).getResponseResult()) - .isEqualTo(convertedHighLevelResult.getModificationResult(ACT_MULTIPLE).getResponseResult()); - assertThat(expectedHighLevelResult.getModificationResult(ACT_MULTIPLE).getResponseHeaders()) - .isEqualTo(convertedHighLevelResult.getModificationResult(ACT_MULTIPLE).getResponseHeaders()); - } - - @Test - @SneakyThrows - void testBatchOpenConnection() - { - final int N = MAX_PARALLEL_CONNECTIONS * 2; - final List inputStreams = new ArrayList<>(); - - final HttpDestination dest = DefaultHttpDestination.builder("").build(); - final HttpClient httpClient = mock(HttpClient.class); - when(httpClient.executeOpen(isNull(), argThat(req -> req instanceof HttpPost), isNull())).thenAnswer(args -> { - final BasicClassicHttpResponse response = new BasicClassicHttpResponse(200, "ok"); - final InputStream inStream = mock(InputStream.class); - inputStreams.add(inStream); - response.setEntity(new InputStreamEntity(inStream, ContentType.APPLICATION_JSON)); - return response; - }); - - // configure test setup - ODataApacheHttpClient5Accessor.setHttpClientFactory(( anyDestination ) -> httpClient); - - // TEST: invoke many batch request each spawning an InputStream - for( int i = 0; i < N; i++ ) { - SERVICE.batch().addReadOperations(READ_ALL).execute(dest); - } - - // ASSERTION: input streams are loaded but never fully consumed - assertThat(inputStreams).hasSize(N); - for( final InputStream inStream : inputStreams ) { - Mockito.verify(inStream, never()).close(); - } - - // TEST: invoke one batch request using try-with-resource - try( BatchResponse result = SERVICE.batch().addReadOperations(READ_ALL).execute(dest) ) { - - assertThat(result).isNotNull(); - } - - // ASSERTION: input stream is fully consumed - Mockito.verify(inputStreams.get(N), times(1)).close(); - - // reset test setup - ODataApacheHttpClient5Accessor.setHttpClientFactory(null); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ODatav4BatchConnectionTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ODatav4BatchConnectionTest.java deleted file mode 100644 index b824a7070d..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/ODatav4BatchConnectionTest.java +++ /dev/null @@ -1,177 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.sap.cloud.sdk.datamodel.odatav4.TestUtility.readResourceFileCrlf; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nonnull; - -import org.apache.hc.core5.http.ConnectionRequestTimeoutException; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.github.tomakehurst.wiremock.matching.UrlPattern; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5FactoryBuilder; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataConnectionException; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataServiceErrorException; - -@WireMockTest -class ODatav4BatchConnectionTest -{ - private static final String RESPONSE_CONTENT_TYPE = - "multipart/mixed; boundary=batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef"; - private static final String RESPONSE_WITH_CHANGESET = - readResourceFileCrlf(ODatav4BatchConnectionTest.class, "BatchResponseWithChangeset.txt"); - private static final String RESPONSE_WITHOUT_CHANGESET = - readResourceFileCrlf(ODatav4BatchConnectionTest.class, "BatchResponseWithoutChangeset.txt"); - private static final String RESPONSE_WITH_ERROR = - readResourceFileCrlf(ODatav4BatchConnectionTest.class, "BatchResponseWithError.txt"); - private static final TestEntityService SERVICE = new TestEntityService() - { - }; - private static final int MAX_PARALLEL_CONNECTIONS = 10; - - private DefaultHttpDestination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - stubFor(head(UrlPattern.ANY).willReturn(noContent())); - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - ODataApacheHttpClient5Accessor - .setHttpClientFactory( - new ApacheHttpClient5FactoryBuilder() - .maxConnectionsTotal(MAX_PARALLEL_CONNECTIONS) - .maxConnectionsPerRoute(MAX_PARALLEL_CONNECTIONS) - .build()); - } - - @AfterEach - void teardown() - { - ODataApacheHttpClient5Accessor.setHttpClientFactory(null); - } - - @Test - void testNoConnectionTimeoutWhenBatchResponseContainsNoChangeset() - { - stubFor(post(UrlPattern.ANY).willReturn(okForContentType(RESPONSE_CONTENT_TYPE, RESPONSE_WITHOUT_CHANGESET))); - - for( int i = 0; i < MAX_PARALLEL_CONNECTIONS * 2; i++ ) { - final GetAllRequestBuilder READ_ALL = SERVICE.getTestEntities(); - final GetByKeyRequestBuilder READ_BY_KEY = SERVICE.getTestEntitiesByKey("foobar"); - final BatchResponse batchResponse = - SERVICE.batch().addReadOperations(READ_ALL, READ_BY_KEY).execute(destination); - - final List readAllResult = batchResponse.getReadResult(READ_ALL); - assertThat(readAllResult).isNotEmpty(); - - final TestEntity readByKeyResult = batchResponse.getReadResult(READ_BY_KEY); - assertThat(readByKeyResult).isNotNull(); - } - } - - @Test - void testNoConnectionTimeoutWhenBatchResponseContainsChangeset() - { - stubFor(post(UrlPattern.ANY).willReturn(okForContentType(RESPONSE_CONTENT_TYPE, RESPONSE_WITH_CHANGESET))); - - for( int i = 0; i < MAX_PARALLEL_CONNECTIONS * 2; i++ ) { - final GetAllRequestBuilder READ_ALL = SERVICE.getTestEntities(); - final GetByKeyRequestBuilder READ_BY_KEY = SERVICE.getTestEntitiesByKey("foobar"); - final CreateRequestBuilder CREATE = SERVICE.createTestEntity(new TestEntity()); - final UpdateRequestBuilder UPDATE = - SERVICE.updateTestEntity(TestEntity.builder().id("upd").build()); - final DeleteRequestBuilder DELETE = - SERVICE.deleteTestEntity(TestEntity.builder().id("del").build()); - final BatchResponse batchResponse = - SERVICE - .batch() - .addReadOperations(READ_ALL, READ_BY_KEY) - .addChangeset(CREATE, UPDATE, DELETE) - .execute(destination); - - final List readAllResult = batchResponse.getReadResult(READ_ALL); - assertThat(readAllResult).isNotEmpty(); - - final TestEntity readByKeyResult = batchResponse.getReadResult(READ_BY_KEY); - assertThat(readByKeyResult).isNotNull(); - - final ModificationResponse createResult = batchResponse.getModificationResult(CREATE); - assertThat(createResult).isNotNull(); - - final ModificationResponse updateResult = batchResponse.getModificationResult(UPDATE); - assertThat(updateResult).isNotNull(); - assertThat(updateResult.getResponseEntity()).isNotNull(); - - final ModificationResponse deleteResult = batchResponse.getModificationResult(DELETE); - assertThat(deleteResult).isNotNull(); - assertThat(deleteResult.getResponseEntity()).isEmpty(); - } - } - - @Test - void testNoConnectionTimeoutWhenBatchResponseContainsError() - { - stubFor(post(UrlPattern.ANY).willReturn(okForContentType(RESPONSE_CONTENT_TYPE, RESPONSE_WITH_ERROR))); - - for( int i = 0; i < MAX_PARALLEL_CONNECTIONS * 2; i++ ) { - final GetAllRequestBuilder READ_ALL = SERVICE.getTestEntities(); - final GetByKeyRequestBuilder READ_BY_KEY = SERVICE.getTestEntitiesByKey("foobar"); - final DeleteRequestBuilder DELETE = - SERVICE.deleteTestEntity(TestEntity.builder().id("del").build()); - final BatchResponse batchResponse = - SERVICE.batch().addReadOperations(READ_ALL, READ_BY_KEY).addChangeset(DELETE).execute(destination); - - final List readAllResult = batchResponse.getReadResult(READ_ALL); - assertThat(readAllResult).isNotEmpty(); - - final TestEntity readByKeyResult = batchResponse.getReadResult(READ_BY_KEY); - assertThat(readByKeyResult).isNotNull(); - - assertThatExceptionOfType(ODataServiceErrorException.class) - .isThrownBy(() -> batchResponse.getModificationResult(DELETE)) - .satisfies(e -> assertThat(e.getHttpCode()).isEqualTo(400)); - } - } - - @Test - @Timeout( value = 300_000L, unit = TimeUnit.MILLISECONDS ) - @Disabled( "Test triggers a ConnectionPoolTimeoutException. Use it only to manually verify behaviour." ) - void testConnectionTimeoutWhenBatchResponseIsNotConsumedFully() - { - stubFor(post(UrlPattern.ANY).willReturn(okForContentType(RESPONSE_CONTENT_TYPE, RESPONSE_WITHOUT_CHANGESET))); - - final GetAllRequestBuilder READ_ALL = SERVICE.getTestEntities(); - final GetByKeyRequestBuilder READ_BY_KEY = SERVICE.getTestEntitiesByKey("foobar"); - - for( int i = 0; i < MAX_PARALLEL_CONNECTIONS; ++i ) { - assertThatNoException() - .isThrownBy(() -> SERVICE.batch().addReadOperations(READ_ALL, READ_BY_KEY).execute(destination)); - } - - assertThatThrownBy(() -> SERVICE.batch().addReadOperations(READ_ALL, READ_BY_KEY).execute(destination)) - .isInstanceOf(ODataConnectionException.class) - .hasRootCauseExactlyInstanceOf(ConnectionRequestTimeoutException.class) - .hasMessageContaining( - "Please execute your request with try-with-resources to ensure resources are properly closed."); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/RequestBuilderEtagParsingTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/RequestBuilderEtagParsingTest.java deleted file mode 100644 index 8b00f9ab34..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/RequestBuilderEtagParsingTest.java +++ /dev/null @@ -1,371 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.ContentType; -import org.apache.hc.core5.http.HttpHeaders; -import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5CacheBuilder; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestinationProperties; -import com.sap.cloud.sdk.datamodel.odata.client.ODataApacheHttpClient5Accessor; - -import lombok.Getter; -import lombok.SneakyThrows; - -class RequestBuilderEtagParsingTest -{ - private static final String SERVICE_PATH = "/service"; - private static final String ENTITY_COLLECTION = "/EntityCollection"; - private static final HttpDestinationProperties DESTINATION = DefaultHttpDestination.builder("http://1").build(); - private static final String FUNCTION_NAME = "FUNCTION_NAME"; - private static final String ETAG_HEAD = "foo"; - private static final String ETAG_BODY = "bar"; - private static final Map HEADERS_NONE = Collections.emptyMap(); - private static final Map HEADERS_WITH_ETAG = Collections.singletonMap("Etag", ETAG_HEAD); - - private HttpClient httpClient; - - @BeforeEach - void setupConnectivity() - { - httpClient = mock(HttpClient.class); - ODataApacheHttpClient5Accessor - .setHttpClientCache(new ApacheHttpClient5CacheBuilder().durationInMilliseconds(0).build()); - ODataApacheHttpClient5Accessor.setHttpClientFactory(dest -> { - assertThat(dest).isSameAs(DESTINATION); - return httpClient; - }); - } - - @AfterEach - void teardownConnectivity() - { - ODataApacheHttpClient5Accessor - .setHttpClientCache(new ApacheHttpClient5CacheBuilder().duration(Duration.ofMinutes(5)).build()); - ODataApacheHttpClient5Accessor.setHttpClientFactory(null); - } - - /** - * ETag via HTTP header. - */ - @SneakyThrows - @Test - void testParseNoEtagGetByKey() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response("{}", HEADERS_NONE)); - - final TestEntity entity = - new GetByKeyRequestBuilder<>( - SERVICE_PATH, - TestEntity.class, - Collections.singletonMap("key", "val"), - ENTITY_COLLECTION).execute(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).isEmpty(); - } - - @SneakyThrows - @Test - void testParseNoEtagFunctionAction() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response(String.format("{%s:{}}", FUNCTION_NAME), HEADERS_NONE)); - - // GET - TestEntity entity = - new SingleValueFunctionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, TestEntity.class).execute(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).isEmpty(); - - // POST - entity = - new SingleValueActionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, TestEntity.class) - .execute(DESTINATION) - .getResponseResult() - .get(); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).isEmpty(); - } - - @SneakyThrows - @Test - void testParseNoEtagUpdate() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response("{}", HEADERS_NONE)); - - final TestEntity requestEntity = new TestEntity(); - - final ModificationResponse result = - new UpdateRequestBuilder<>(SERVICE_PATH, requestEntity, ENTITY_COLLECTION).execute(DESTINATION); - - assertThat(result).isNotNull(); - assertThat(result.getModifiedEntity()).isNotNull(); - assertThat(result.getResponseEntity()).isNotNull().isNotEmpty(); - assertThat(result.getRequestEntity()).isNotNull().isSameAs(requestEntity); - - assertThat(result.getModifiedEntity().getVersionIdentifier()).isEmpty(); - assertThat(result.getResponseEntity().get().getVersionIdentifier()).isEmpty(); - assertThat(result.getRequestEntity().getVersionIdentifier()).isEmpty(); - } - - @SneakyThrows - @Test - void testParseHeaderEtagGetByKey() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response("{}", HEADERS_WITH_ETAG)); - - final TestEntity entity = - new GetByKeyRequestBuilder<>( - SERVICE_PATH, - TestEntity.class, - Collections.singletonMap("key", "val"), - ENTITY_COLLECTION).execute(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - } - - @SneakyThrows - @Test - void testParseHeaderEtagFunctionAction() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn(response(String.format("{%s:{}}", FUNCTION_NAME), HEADERS_WITH_ETAG)); - - // GET - TestEntity entity = - new SingleValueFunctionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, TestEntity.class).execute(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - - // POST - entity = - new SingleValueActionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, TestEntity.class) - .execute(DESTINATION) - .getResponseResult() - .get(); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - } - - @SneakyThrows - @Test - void testParseHeaderEtagUpdate() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response("{}", HEADERS_WITH_ETAG)); - - final TestEntity requestEntity = new TestEntity(); - - final ModificationResponse result = - new UpdateRequestBuilder<>(SERVICE_PATH, requestEntity, ENTITY_COLLECTION).execute(DESTINATION); - - assertThat(result).isNotNull(); - assertThat(result.getModifiedEntity()).isNotNull(); - assertThat(result.getResponseEntity()).isNotNull().isNotEmpty(); - assertThat(result.getRequestEntity()).isNotNull().isSameAs(requestEntity); - - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_HEAD); - assertThat(result.getResponseEntity().get().getVersionIdentifier()).containsExactly(ETAG_HEAD); - assertThat(result.getRequestEntity().getVersionIdentifier()).isEmpty(); - } - - /** - * ETag via HTTP payload. - */ - @SneakyThrows - @Test - void testParsePayloadEtagGetAll() - { - final String payload = String.format("{value:[{@etag:\"%s\"}]}", ETAG_BODY); - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response(payload, HEADERS_NONE)); - - final List entities = - new GetAllRequestBuilder<>(SERVICE_PATH, TestEntity.class, ENTITY_COLLECTION).execute(DESTINATION); - - assertThat(entities).isNotNull().hasSize(1); - assertThat(entities.get(0).getVersionIdentifier()).containsExactly(ETAG_BODY); - } - - @SneakyThrows - @Test - void testParsePayloadEtagGetByKey() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response(String.format("{@etag:\"%s\"}", ETAG_BODY), HEADERS_NONE)); - - final TestEntity entity = - new GetByKeyRequestBuilder<>( - SERVICE_PATH, - TestEntity.class, - Collections.singletonMap("key", "val"), - ENTITY_COLLECTION).execute(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_BODY); - } - - @SneakyThrows - @Test - void testParsePayloadEtagFunctionAction() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response(String.format("{@etag:\"%s\"}", ETAG_BODY), HEADERS_NONE)); - - // GET - TestEntity entity = - new SingleValueFunctionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, TestEntity.class).execute(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_BODY); - - // POST - entity = - new SingleValueActionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, TestEntity.class) - .execute(DESTINATION) - .getResponseResult() - .get(); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_BODY); - } - - @SneakyThrows - @Test - void testParsePayloadEtagUpdate() - { - when(httpClient.executeOpen(isNull(), any(), isNull())).thenReturn(response(String.format("{@etag:\"%s\"}", ETAG_BODY), HEADERS_NONE)); - - final TestEntity requestEntity = new TestEntity(); - - final ModificationResponse result = - new UpdateRequestBuilder<>(SERVICE_PATH, requestEntity, ENTITY_COLLECTION).execute(DESTINATION); - - assertThat(result).isNotNull(); - assertThat(result.getModifiedEntity()).isNotNull(); - assertThat(result.getResponseEntity()).isNotNull().isNotEmpty(); - assertThat(result.getRequestEntity()).isNotNull().isSameAs(requestEntity); - - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_BODY); - assertThat(result.getResponseEntity().get().getVersionIdentifier()).containsExactly(ETAG_BODY); - assertThat(result.getRequestEntity().getVersionIdentifier()).isEmpty(); - } - - /** - * ETag via HTTP header + HTTP payload. - */ - @SneakyThrows - @Test - void testParseHeaderAndPayloadEtagGetByKey() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn(response(String.format("{@etag:\"%s\"}", ETAG_BODY), HEADERS_WITH_ETAG)); - - final TestEntity entity = - new GetByKeyRequestBuilder<>( - SERVICE_PATH, - TestEntity.class, - Collections.singletonMap("key", "val"), - ENTITY_COLLECTION).execute(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - } - - @SneakyThrows - @Test - void testParseHeaderAndPayloadEtagFunctionAction() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn(response(String.format("{%s:{@etag:\"%s\"}}", FUNCTION_NAME, ETAG_BODY), HEADERS_WITH_ETAG)); - - // GET - TestEntity entity = - new SingleValueFunctionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, TestEntity.class).execute(DESTINATION); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - - // POST - entity = - new SingleValueActionRequestBuilder<>(SERVICE_PATH, FUNCTION_NAME, TestEntity.class) - .execute(DESTINATION) - .getResponseResult() - .get(); - - assertThat(entity).isNotNull(); - assertThat(entity.getVersionIdentifier()).containsExactly(ETAG_HEAD); - } - - @SneakyThrows - @Test - void testParseHeaderAndPayloadEtagUpdate() - { - when(httpClient.executeOpen(isNull(), any(), isNull())) - .thenReturn(response(String.format("{@etag:\"%s\"}", ETAG_BODY), HEADERS_WITH_ETAG)); - - final TestEntity requestEntity = new TestEntity(); - - final ModificationResponse result = - new UpdateRequestBuilder<>(SERVICE_PATH, requestEntity, ENTITY_COLLECTION).execute(DESTINATION); - - assertThat(result).isNotNull(); - assertThat(result.getModifiedEntity()).isNotNull(); - assertThat(result.getResponseEntity()).isNotNull().isNotEmpty(); - assertThat(result.getRequestEntity()).isNotNull().isSameAs(requestEntity); - - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_HEAD); - assertThat(result.getResponseEntity().get().getVersionIdentifier()).containsExactly(ETAG_HEAD); - assertThat(result.getRequestEntity().getVersionIdentifier()).isEmpty(); - } - - /** - * HELPER METHODS. - */ - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - @JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) - public static class TestEntity extends VdmEntity - { - @Getter - private final String entityCollection = ENTITY_COLLECTION; - @Getter - private final Class type = TestEntity.class; - @Getter - private final String odataType = "Example.TestEntity"; - } - - @Nonnull - static ClassicHttpResponse response( @Nonnull final String payload, @Nonnull final Map headers ) - { - final BasicClassicHttpResponse result = new BasicClassicHttpResponse(200, "Ok"); - result.setEntity(new StringEntity(payload, StandardCharsets.UTF_8)); - result.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString()); - headers.forEach(result::setHeader); - return result; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueActionRequestBuilderTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueActionRequestBuilderTest.java deleted file mode 100644 index ec7ea3ff62..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueActionRequestBuilderTest.java +++ /dev/null @@ -1,316 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.headRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Duration; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odatav4.TestUtility; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@WireMockTest -class SingleValueActionRequestBuilderTest -{ - private static final String DEFAULT_SERVICE_PATH = "/odata/default"; - private static final String ODATA_ACTION = "TestAction"; - - private DefaultHttpDestination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - stubFor(head(anyUrl()).willReturn(ok())); - } - - private static String readResourceFile( final String resourceFileName ) - { - return TestUtility.readResourceFile(SingleValueActionRequestBuilderTest.class, resourceFileName); - } - - @Data - @EqualsAndHashCode( callSuper = true ) - @NoArgsConstructor - @AllArgsConstructor - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - @JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) - public static class TestEntity extends VdmEntity - { - @Getter - private final String odataType = "com.sap.cloud.sdk.TestEntity"; - - @Getter - private final String entityCollection = "EntityCollection"; - - @Getter - private final Class type = TestEntity.class; - - @Override - protected String getDefaultServicePath() - { - return DEFAULT_SERVICE_PATH; - } - - @ElementName( "Name" ) - private String name; - } - - @Builder - @Data - @NoArgsConstructor - @AllArgsConstructor - @ToString( doNotUseGetters = true, callSuper = true ) - @EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - @JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) - public static class ComplexType extends VdmComplex - { - - @Getter - private final String odataType = "com.sap.cloud.sdk.ComplexType"; - - @Getter - private final Class type = ComplexType.class; - - @ElementName( "City" ) - private String city; - - @ElementName( "Country" ) - private String country; - } - - @Test - void testFunctionQueryWithParametersAndNoResponse() - { - final String actionRequestUrl = String.format("%s/%s", DEFAULT_SERVICE_PATH, ODATA_ACTION); - final String actionRequestBody = readResourceFile("ActionParametersRequestBody.json"); - - stubFor(post(urlPathEqualTo(actionRequestUrl)).willReturn(noContent())); - - final Map actionParameters = new LinkedHashMap<>(); - actionParameters.put("stringParameter", "test"); - actionParameters.put("booleanParameter", true); - actionParameters.put("integerParameter", 9000); - actionParameters.put("decimalParameter", 3.14d); - actionParameters.put("durationParameter", Duration.ofHours(8)); - actionParameters - .put("dateTimeOffsetParameter", OffsetDateTime.of(2020, 3, 12, 5, 2, 30, 0, ZoneOffset.of("Z"))); - actionParameters.put("timeOfDayParameter", LocalTime.of(13, 03, 39, 999000000)); - - final SingleValueActionRequestBuilder sut = - new SingleValueActionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_ACTION, actionParameters, Void.class); - assertThat(sut.toRequest().getRelativeUri()).hasToString(actionRequestUrl); - final ActionResponseSingle actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)) - - .withHeader("Content-Type", equalTo("application/json")) - - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult()).isEmpty(); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(204); - } - - @Test - void testActionWithNoParameterAndPrimitiveResponse() - { - final String actionRequestUrl = String.format("%s/%s", DEFAULT_SERVICE_PATH, ODATA_ACTION); - final String actionRequestBody = readResourceFile("ActionNoParametersRequestBody.json"); - - stubFor(post(urlPathEqualTo(actionRequestUrl)).willReturn(okJson("{" + "\"value\" : 3.14" + "}"))); - - final SingleValueActionRequestBuilder sut = - new SingleValueActionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_ACTION, Float.class); - final ActionResponseSingle actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(actionRequestUrl)) - .withHeader("Content-Type", equalTo("application/json")) - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult().get()).isEqualTo(3.14f); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(200); - } - - @Test - void testActionWithNoParameterAndStringResponse() - { - final String actionRequestUrl = String.format("%s/%s", DEFAULT_SERVICE_PATH, ODATA_ACTION); - final String actionRequestBody = readResourceFile("ActionNoParametersRequestBody.json"); - - stubFor(post(urlPathEqualTo(actionRequestUrl)).willReturn(okJson("{" + "\"value\" : \"Works\"" + "}"))); - - final SingleValueActionRequestBuilder sut = - new SingleValueActionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_ACTION, String.class); - final ActionResponseSingle actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_ACTION)) - .withHeader("Content-Type", equalTo("application/json")) - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult().get()).isEqualTo("Works"); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(200); - - } - - @Test - void testActionWithEntityParameterAndEntityResponse() - { - final String actionRequestUrl = String.format("%s/%s", DEFAULT_SERVICE_PATH, ODATA_ACTION); - final String actionRequestBody = readResourceFile("ActionEntityRequestBody.json"); - - stubFor(post(urlPathEqualTo(actionRequestUrl)).willReturn(okJson("{ \"Name\" : \"Tester\" }"))); - - final Map actionParameters = new LinkedHashMap<>(); - final TestEntity testEntity = new TestEntity("Tester"); - actionParameters.put("entityParameter", testEntity); - - final SingleValueActionRequestBuilder sut = - new SingleValueActionRequestBuilder<>( - DEFAULT_SERVICE_PATH, - ODATA_ACTION, - actionParameters, - TestEntity.class); - final ActionResponseSingle actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(actionRequestUrl)) - .withHeader("Content-Type", equalTo("application/json")) - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult().get().getName()).isEqualTo("Tester"); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(200); - } - - @Test - void testActionWithEntityParameterNull() - { - final String actionRequestUrl = String.format("%s/%s", DEFAULT_SERVICE_PATH, ODATA_ACTION); - final String actionRequestBody = readResourceFile("ActionNullEntityRequestBody.json"); - - stubFor(post(urlPathEqualTo(actionRequestUrl)).willReturn(okJson("{ \"Name\" : \"Tester\" }"))); - - final Map actionParameters = new LinkedHashMap<>(); - actionParameters.put("entityParameter", null); - - final SingleValueActionRequestBuilder sut = - new SingleValueActionRequestBuilder<>( - DEFAULT_SERVICE_PATH, - ODATA_ACTION, - actionParameters, - TestEntity.class); - final ActionResponseSingle actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(actionRequestUrl)) - .withHeader("Content-Type", equalTo("application/json")) - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult().get().getName()).isEqualTo("Tester"); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(200); - } - - @Test - void testActionWithComplexTypeParameterComplexTypeResponse() - { - final String actionRequestUrl = String.format("%s/%s", DEFAULT_SERVICE_PATH, ODATA_ACTION); - final String actionRequestBody = readResourceFile("ActionComplexTypeRequestBody.json"); - - stubFor( - post(urlPathEqualTo(actionRequestUrl)) - .willReturn(okJson("{\"City\": \"Stockholm\",\"Country\": \"Sweden\"}"))); - - final Map actionParameters = new LinkedHashMap<>(); - final ComplexType testComplexType = new ComplexType("Stockholm", "Sweden"); - actionParameters.put("complexEntity", testComplexType); - - final SingleValueActionRequestBuilder sut = - new SingleValueActionRequestBuilder<>( - DEFAULT_SERVICE_PATH, - ODATA_ACTION, - actionParameters, - ComplexType.class); - final ActionResponseSingle actualResponse = sut.execute(destination); - - verify( - postRequestedFor(urlEqualTo(actionRequestUrl)) - .withHeader("Content-Type", equalTo("application/json")) - .withRequestBody(equalToJson(actionRequestBody))); - - verify( - 1, - headRequestedFor(urlEqualTo(DEFAULT_SERVICE_PATH + "/")).withHeader("x-csrf-token", equalTo("fetch"))); - - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getResponseResult().get().getCity()).isEqualTo("Stockholm"); - assertThat(actualResponse.getResponseResult().get().getCountry()).isEqualTo("Sweden"); - assertThat(actualResponse.getResponseStatusCode()).isEqualTo(200); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueFunctionRequestBuilderTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueFunctionRequestBuilderTest.java deleted file mode 100644 index 64419a3c6a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/SingleValueFunctionRequestBuilderTest.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.Duration; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.ODataResourcePath; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataFunctionParameters; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@WireMockTest -class SingleValueFunctionRequestBuilderTest -{ - private static final String DEFAULT_SERVICE_PATH = "/odata/default"; - private static final String ODATA_FUNCTION = "TestFunction"; - private static final ODataFunctionParameters FUNCTION_PARAMETERS; - private static final Map FUNCTION_PARAMETER_MAP = new HashMap<>(); - - static { - FUNCTION_PARAMETER_MAP.put("stringParameter", "test"); - FUNCTION_PARAMETER_MAP.put("booleanParameter", true); - FUNCTION_PARAMETER_MAP.put("integerParameter", 9000); - FUNCTION_PARAMETER_MAP.put("decimalParameter", 3.14); - FUNCTION_PARAMETER_MAP.put("durationParameter", Duration.ofHours(8)); - FUNCTION_PARAMETER_MAP.put("dateTimeParameter", LocalDateTime.of(2019, 12, 25, 8, 0, 0)); - - FUNCTION_PARAMETERS = ODataFunctionParameters.of(FUNCTION_PARAMETER_MAP, ODataProtocol.V4); - } - - private static final String ODATA_FUNCTION_PARAMETER_WITH_SPECIAL_CHARACTER = "(stringParameter='t''est')"; - - private DefaultHttpDestination destination; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - } - - @Data - @EqualsAndHashCode( callSuper = true ) - @NoArgsConstructor - @AllArgsConstructor - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - @JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) - public static class TestEntity extends VdmEntity - { - @Getter - private final String odataType = "com.sap.cloud.sdk.TestEntity"; - - @Getter - private final String entityCollection = "EntityCollection"; - - @Getter - private final Class type = TestEntity.class; - - @Override - protected String getDefaultServicePath() - { - return DEFAULT_SERVICE_PATH; - } - - @ElementName( "Name" ) - @SerializedName( "Name" ) - @JsonProperty( "Name" ) - private String name; - } - - @Test - void testFunctionQueryWithoutParameters() - { - final SingleValueFunctionRequestBuilder requestBuilder = - new SingleValueFunctionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_FUNCTION, Void.class); - - assertThat(requestBuilder.toRequest().getRelativeUri()) - .hasToString(DEFAULT_SERVICE_PATH + '/' + ODATA_FUNCTION + "()"); - } - - @Test - void testFunctionQueryWithMethodParameters() - { - final SingleValueFunctionRequestBuilder requestBuilder = - new SingleValueFunctionRequestBuilder<>( - DEFAULT_SERVICE_PATH, - ODATA_FUNCTION, - FUNCTION_PARAMETER_MAP, - Void.class); - - assertThat(requestBuilder.toRequest().getRelativeUri()) - .hasToString(DEFAULT_SERVICE_PATH + '/' + ODATA_FUNCTION + FUNCTION_PARAMETERS.toEncodedString()); - } - - @Test - void testFunctionQueryWithSpecialCharactersInMethodParameters() - { - final ODataFunctionParameters parameters = - new ODataFunctionParameters(ODataProtocol.V4).addParameter("stringParameter", "t'est"); - final ODataResourcePath functionPath = ODataResourcePath.of(ODATA_FUNCTION, parameters); - - final SingleValueFunctionRequestBuilder requestBuilder = - new SingleValueFunctionRequestBuilder<>(DEFAULT_SERVICE_PATH, functionPath, Void.class); - - assertThat(requestBuilder.toRequest().getRelativeUri()) - .hasToString(DEFAULT_SERVICE_PATH + '/' + ODATA_FUNCTION + ODATA_FUNCTION_PARAMETER_WITH_SPECIAL_CHARACTER); - } - - @Test - void testFunctionQueryWithMapParameters() - { - final SingleValueFunctionRequestBuilder requestBuilder = - new SingleValueFunctionRequestBuilder<>( - DEFAULT_SERVICE_PATH, - ODATA_FUNCTION, - FUNCTION_PARAMETER_MAP, - Void.class); - assertThat(requestBuilder.toRequest().getRelativeUri()) - .hasToString(DEFAULT_SERVICE_PATH + '/' + ODATA_FUNCTION + FUNCTION_PARAMETERS.toEncodedString()); - } - - @Test - void testFunctionWithPrimitiveResponse() - { - stubFor( - get(urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_FUNCTION + "()")) - .willReturn(okJson("{" + "\"value\" : 3.14" + "}"))); - - final SingleValueFunctionRequestBuilder requestBuilder = - new SingleValueFunctionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_FUNCTION, Float.class); - - final Float actualResponse = requestBuilder.execute(destination); - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse).isEqualTo(3.14f); - } - - @Test - void testFunctionWithStringResponse() - { - stubFor( - get(urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_FUNCTION + "()")) - .willReturn(okJson("{" + "\"value\" : \"Works\"" + "}"))); - - final SingleValueFunctionRequestBuilder requestBuilder = - new SingleValueFunctionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_FUNCTION, String.class); - - final String actualResponse = requestBuilder.execute(destination); - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse).isEqualTo("Works"); - } - - @Test - void testFunctionWithEntityResponse() - { - stubFor( - get(urlPathEqualTo(DEFAULT_SERVICE_PATH + '/' + ODATA_FUNCTION + "()")) - .willReturn(okJson("{" + "\"Name\" : \"Tester\"" + "}"))); - - final SingleValueFunctionRequestBuilder requestBuilder = - new SingleValueFunctionRequestBuilder<>(DEFAULT_SERVICE_PATH, ODATA_FUNCTION, TestEntity.class); - - final TestEntity actualResponse = requestBuilder.execute(destination); - assertThat(actualResponse).isNotNull(); - assertThat(actualResponse.getName()).isEqualTo("Tester"); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/TestEntity.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/TestEntity.java deleted file mode 100644 index 6510520482..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/TestEntity.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.ImmutableMap; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class TestEntity extends VdmEntity -{ - static final String SERVICE_PATH = "/odata/default"; - - @Getter - private final String odataType = "TestEntity"; - - @Nonnull - @Override - protected String getEntityCollection() - { - return "EntityCollection"; - } - - @Nonnull - @Override - public Class getType() - { - return TestEntity.class; - } - - @Override - protected String getDefaultServicePath() - { - return SERVICE_PATH; - } - - @Nullable - @ElementName( "id" ) - private String id; - - public final static SimpleProperty.String ID = new SimpleProperty.String<>(TestEntity.class, "id"); - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey key = super.getKey(); - key.addKeyProperty("id", getId()); - return key; - } - - public void setId( @Nullable final String id ) - { - rememberChangedField("id", this.id); - this.id = id; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - return ImmutableMap. builder().putAll(super.toMapOfFields()).put("id", getId()).build(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/TestEntityService.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/TestEntityService.java deleted file mode 100644 index c297bdf5a4..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/TestEntityService.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; - -import javax.annotation.Nonnull; - -import com.google.common.collect.ImmutableMap; - -interface TestEntityService -{ - String SERVICE_PATH = "/"; - - @Nonnull - default GetAllRequestBuilder getTestEntities() - { - return new GetAllRequestBuilder<>(SERVICE_PATH, TestEntity.class, "EntityCollection"); - } - - @Nonnull - default CountRequestBuilder countTestEntities() - { - return new CountRequestBuilder<>(SERVICE_PATH, TestEntity.class, "EntityCollection"); - } - - @Nonnull - default GetByKeyRequestBuilder getTestEntitiesByKey( @Nonnull final String key ) - { - return new GetByKeyRequestBuilder<>( - SERVICE_PATH, - TestEntity.class, - ImmutableMap.of("key", key), - "EntityCollection"); - } - - @Nonnull - default CreateRequestBuilder createTestEntity( @Nonnull final TestEntity testEntity ) - { - return new CreateRequestBuilder<>(SERVICE_PATH, testEntity, "EntityCollection"); - } - - @Nonnull - default DeleteRequestBuilder deleteTestEntity( @Nonnull final TestEntity testEntity ) - { - return new DeleteRequestBuilder<>(SERVICE_PATH, testEntity, "EntityCollection"); - } - - @Nonnull - default UpdateRequestBuilder updateTestEntity( @Nonnull final TestEntity testEntity ) - { - return new UpdateRequestBuilder<>(SERVICE_PATH, testEntity, "EntityCollection"); - } - - @Nonnull - default SingleValueActionRequestBuilder actionSingleResult() - { - return new SingleValueActionRequestBuilder<>( - SERVICE_PATH, - "action-single", - ImmutableMap.of("secret", "pass"), - Integer.class); - } - - @Nonnull - default CollectionValueActionRequestBuilder actionMultipleResult() - { - return new CollectionValueActionRequestBuilder<>( - SERVICE_PATH, - "action-multiple", - ImmutableMap.of("secret", "pass"), - String.class); - } - - @Nonnull - default SingleValueFunctionRequestBuilder functionSingleResult() - { - return new SingleValueFunctionRequestBuilder<>( - SERVICE_PATH, - "function-single", - ImmutableMap.of("secret", "pass"), - Integer.class); - } - - @Nonnull - default CollectionValueFunctionRequestBuilder functionMultipleResult() - { - return new CollectionValueFunctionRequestBuilder<>( - SERVICE_PATH, - "function-multiple", - ImmutableMap.of("secret", "pass"), - String.class); - } - - @Nonnull - default BatchRequestBuilder batch() - { - final AtomicInteger uuidCounter = new AtomicInteger(); - return new BatchRequestBuilder(SERVICE_PATH) - { - @Override - protected Supplier getUuidProvider() - { - return () -> new UUID(0, uuidCounter.incrementAndGet()); - } - }; - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPatchTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPatchTest.java deleted file mode 100644 index e69b2a8294..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPatchTest.java +++ /dev/null @@ -1,633 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.skyscreamer.jsonassert.JSONCompareMode; - -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -class UpdateRequestHelperPatchTest -{ - private static final String TEST_DEFAULT_SERVICE_PATH = "/odata/default"; - - @NoArgsConstructor - @AllArgsConstructor - @Builder - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - private static class TestEntity extends VdmEntity - { - @Getter - private final String odataType = "TestEntity"; - - @Getter - private final String entityCollection = "EntityParentCollection"; - - @Getter - private final String defaultServicePath = TEST_DEFAULT_SERVICE_PATH; - - @Getter - private final Class type = TestEntity.class; - - @Getter - @ElementName( "Neighbor" ) - private TestEntity neighbor; - - @Getter - @ElementName( "Value" ) - private BigDecimal value; - - @Getter - @ElementName( "Emails" ) - private List emails; - - @Getter - @ElementName( "Name" ) - private String name; - - @Getter - @ElementName( "EffortValue" ) - private Double effortValue; - - @Getter - @ElementName( "ShoeSize" ) - private Integer shoeSize; - - @Getter - @ElementName( "IsRetired" ) - private Boolean isRetired; - - @Getter - @ElementName( "BirthDate" ) - private LocalDate birthDate; - - @Getter - @ElementName( "FavouriteCharacter" ) - private Character favouriteCharacter; - - @Getter - @ElementName( "ComplexProperty" ) - private TestComplex complexProperty; - - @Getter - @ElementName( "ComplexProperties" ) - private Collection complexProperties; - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey key = new ODataEntityKey(ODataProtocol.V4); - key.addKeyProperty("Name", name); - return key; - } - - public void setNeighbor( TestEntity neighbor ) - { - rememberChangedField("Neighbor", this.neighbor); - this.neighbor = neighbor; - } - - public void setName( String name ) - { - rememberChangedField("Name", this.name); - this.name = name; - } - - public void setEffortValue( Double effortValue ) - { - rememberChangedField("EffortValue", this.effortValue); - this.effortValue = effortValue; - } - - public void setValue( BigDecimal value ) - { - rememberChangedField("Value", this.value); - this.value = value; - } - - public void setEmails( List emails ) - { - rememberChangedField("Emails", this.emails); - this.emails = emails; - } - - public void setShoeSize( Integer shoeSize ) - { - rememberChangedField("ShoeSize", this.shoeSize); - this.shoeSize = shoeSize; - } - - public void setIsRetired( Boolean isRetired ) - { - rememberChangedField("IsRetired", this.isRetired); - this.isRetired = isRetired; - } - - public void setBirthDate( LocalDate birthDate ) - { - rememberChangedField("BirthDate", this.birthDate); - this.birthDate = birthDate; - } - - public void setFavouriteCharacter( Character favouriteCharacter ) - { - rememberChangedField("FavouriteCharacter", this.favouriteCharacter); - this.favouriteCharacter = favouriteCharacter; - } - - public void setComplexProperty( TestComplex complexProperty ) - { - rememberChangedField("ComplexProperty", this.complexProperty); - this.complexProperty = complexProperty; - } - - public void setComplexProperties( Collection complexProperties ) - { - rememberChangedField("ComplexProperties", this.complexProperties); - this.complexProperties = complexProperties; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map result = new HashMap<>(); - result.put("Name", name); - result.put("Value", value); - result.put("Neighbor", neighbor); - result.put("Emails", emails); - result.put("EffortValue", effortValue); - result.put("ShoeSize", shoeSize); - result.put("IsRetired", isRetired); - result.put("BirthDate", birthDate); - result.put("FavouriteCharacter", favouriteCharacter); - result.put("ComplexProperty", complexProperty); - result.put("ComplexProperties", complexProperties); - return result; - } - } - - @Builder - @NoArgsConstructor - @AllArgsConstructor - private static class TestComplex extends VdmComplex - { - @Getter - private final String odataType = "TestComplex"; - - @Getter - @ElementName( "StringProperty" ) - private String stringProperty; - - @Getter - @ElementName( "ComplexProperty" ) - private TestComplex complexProperty; - - @Nonnull - @Override - public Class getType() - { - return TestComplex.class; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("StringProperty", getStringProperty()); - values.put("ComplexProperty", getComplexProperty()); - return values; - } - - public void setStringProperty( String stringProperty ) - { - rememberChangedField("StringProperty", this.stringProperty); - this.stringProperty = stringProperty; - } - - public void setComplexProperty( TestComplex complexProperty ) - { - rememberChangedField("ComplexProperty", this.complexProperty); - this.complexProperty = complexProperty; - } - } - - @Test - void testSimpleBigDecimalPropertyPatchPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").build(); - - entity.setValue(new BigDecimal("0.00000000001")); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"Value\":0.00000000001,\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testSimpleDoublePropertyPatchPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").build(); - - entity.setEffortValue(42d); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"EffortValue\":42,\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testSimpleIntegerPropertyPatchPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").build(); - - entity.setShoeSize(46); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"ShoeSize\":46,\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testSimpleStringPropertyPatchPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").build(); - - entity.setName("Bar"); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"Name\":\"Bar\",\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testSimpleLocalDatePropertyPatchPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").build(); - - entity.setBirthDate(LocalDate.MIN); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"BirthDate\":\"-999999999-01-01\",\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testSimpleSingleCharacterDatePropertyPatchPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").build(); - - entity.setFavouriteCharacter('A'); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"FavouriteCharacter\":\"A\",\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testSimpleBooleanPropertyPatchPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").build(); - - entity.setIsRetired(Boolean.FALSE); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"IsRetired\":false,\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testDirectSimpleCollectionPropertyPatchPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").build(); - - entity.setEmails(Lists.newArrayList("foo@sap.com")); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"Emails\":[\"foo@sap.com\"],\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Disabled( "Indirect changes on entity properties are not tracked. CLOUDECOSYSTEM-8217" ) - // @Test - void testIndirectCollectionPropertyViaGetPatchPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").emails(Lists.newArrayList()).build(); - - entity.getEmails().add("foo@sap.com"); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"Emails\":[\"foo@sap.com\"],\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testNavigationPropertyExistingPatchPayload() - throws Exception - { - final TestEntity entityFoo = TestEntity.builder().name("Foo").value(BigDecimal.ZERO).build(); - final TestEntity entityBar = TestEntity.builder().name("Bar").value(BigDecimal.ONE).build(); - - entityFoo.setNeighbor(entityBar); - - final String json = new UpdateRequestHelperPatch().toJson(entityFoo, Collections.emptySet()); - assertEquals( - "{\"Neighbor\":{\"@id\":\"EntityParentCollection('Bar')\"},\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testNavigationPropertyNewPatchPayload() - throws Exception - { - final TestEntity entityFoo = TestEntity.builder().name("Foo").value(BigDecimal.ZERO).build(); - - final TestEntity entityBar = new TestEntity(); - entityBar.setName("Bar"); - entityBar.setValue(BigDecimal.ONE); - - entityFoo.setNeighbor(entityBar); - - final String json = new UpdateRequestHelperPatch().toJson(entityFoo, Collections.emptySet()); - - assertEquals( - "{\"Neighbor\":{\"Value\":1,\"Name\":\"Bar\"},\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testNavigationPropertyChangePatchPayload() - throws Exception - { - final TestEntity entityFoo = TestEntity.builder().name("Foo").value(BigDecimal.ZERO).build(); - final TestEntity entityBar = TestEntity.builder().name("Bar").build(); - entityBar.setValue(BigDecimal.ONE); - entityFoo.setNeighbor(entityBar); - - final String json = new UpdateRequestHelperPatch().toJson(entityFoo, Collections.emptySet()); - - assertEquals( - "{\"Neighbor\":{\"Value\":1,\"@id\":\"EntityParentCollection('Bar')\"},\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testNavigationPropertyCyclicPatchPayload() - throws Exception - { - final TestEntity entityBar = new TestEntity(); - entityBar.setName("Bar"); - - final TestEntity entityFoo = new TestEntity(); - entityFoo.setName("Foo"); - entityFoo.setNeighbor(entityBar); - entityBar.setNeighbor(entityFoo); - - final String json = new UpdateRequestHelperPatch().toJson(entityFoo, Collections.emptySet()); - assertEquals(""" - {\ - "Neighbor":{\ - "Neighbor":{"@id":"EntityParentCollection('Foo')"},\ - "Name":"Bar"\ - },\ - "Name":"Foo",\ - "@odata.type":"#TestEntity"\ - }\ - """, json, JSONCompareMode.LENIENT); - } - - @Test - void testSetComplexMember() - throws Exception - { - final TestComplex complex = TestComplex.builder().stringProperty("Foo").build(); - complex.setCustomField("customField", "customValue"); - - final TestEntity entity = TestEntity.builder().build(); - entity.setComplexProperty(complex); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"ComplexProperty\":{\"StringProperty\":\"Foo\",\"customField\":\"customValue\"},\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testRemoveComplexMember() - throws Exception - { - final TestEntity entity = - TestEntity.builder().complexProperty(TestComplex.builder().stringProperty("Foo").build()).build(); - entity.setComplexProperty(null); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"ComplexProperty\":null,\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testUpdateFieldOfComplexMember() - throws Exception - { - final TestEntity entity = TestEntity.builder().complexProperty(new TestComplex()).build(); - entity.getComplexProperty().setStringProperty("Foo"); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"ComplexProperty\":{\"StringProperty\":\"Foo\"},\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testRemoveFieldOfComplexMember() - throws Exception - { - final TestComplex complex = TestComplex.builder().stringProperty("Foo").build(); - complex.setCustomField("customField", "customValue"); - final TestEntity entity = TestEntity.builder().complexProperty(complex).build(); - entity.getComplexProperty().setStringProperty(null); - entity.getComplexProperty().setCustomField("customField", null); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"ComplexProperty\":{\"StringProperty\":null,\"customField\":null},\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testUpdateFieldOfNestedComplexMember() - throws Exception - { - final TestComplex complexChild = new TestComplex(); - final TestComplex complexParent = - TestComplex.builder().stringProperty("Parent").complexProperty(complexChild).build(); - final TestEntity entity = TestEntity.builder().complexProperty(complexParent).build(); - complexChild.setStringProperty("Hello, World!"); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"ComplexProperty\":{\"StringProperty\":\"Parent\",\"ComplexProperty\":{\"StringProperty\":\"Hello, World!\"}},\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testRemoveFieldOfNestedComplexMember() - throws Exception - { - final TestComplex complexChild = TestComplex.builder().stringProperty("Child").build(); - final TestComplex complexParent = - TestComplex.builder().stringProperty("Parent").complexProperty(complexChild).build(); - final TestEntity entity = TestEntity.builder().complexProperty(complexParent).build(); - entity.getComplexProperty().getComplexProperty().setStringProperty(null); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"ComplexProperty\":{\"StringProperty\":\"Parent\",\"ComplexProperty\":{\"StringProperty\":null}},\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testSetNullableCollectionToEmpty() - throws Exception - { - final TestEntity entity = TestEntity.builder().build(); - assertThat(entity.getComplexProperties()).isNull(); - entity.setComplexProperties(new ArrayList<>()); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"ComplexProperties\":[],\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testSetEmptyToCollectionToEmpty() - throws Exception - { - final TestEntity entity = TestEntity.builder().complexProperties(new ArrayList<>()).build(); - entity.setComplexProperties(new ArrayList<>()); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testRemoveCollection() - throws Exception - { - final TestEntity entity = TestEntity.builder().complexProperties(new ArrayList<>()).build(); - entity.setComplexProperties(null); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"ComplexProperties\":null,\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } - - @Test - void testSetCollectionOfComplexProperties() - throws Exception - { - final TestComplex complex = TestComplex.builder().stringProperty("Foo").build(); - complex.setCustomField("customField", "customValue"); - - final TestEntity entity = TestEntity.builder().complexProperties(new ArrayList<>()).build(); - entity.setComplexProperties(Collections.singletonList(complex)); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"ComplexProperties\":[{\"StringProperty\":\"Foo\",\"ComplexProperty\":null,\"customField\":\"customValue\"}],\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testUpdateComplexMemberInCollection() - throws Exception - { - final TestComplex collectionMember = new TestComplex(); - final TestEntity entity = TestEntity.builder().complexProperties(Lists.newArrayList(collectionMember)).build(); - collectionMember.setStringProperty("Foo"); - collectionMember.setCustomField("customField", "customValue"); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"ComplexProperties\":[{\"StringProperty\":\"Foo\",\"customField\":\"customValue\"}],\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testRemoveFieldOfComplexMemberInCollection() - throws Exception - { - final TestComplex collectionMember = TestComplex.builder().stringProperty("Foo").build(); - final TestEntity entity = TestEntity.builder().complexProperties(Lists.newArrayList(collectionMember)).build(); - collectionMember.setStringProperty(null); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"ComplexProperties\":[{\"StringProperty\":null}],\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Disabled( "Tracking changes within a collection of complex properties is not yet supported. CLOUDECOSYSTEM-8217" ) - // @Test - void testAddMemberToComplexCollection() - throws Exception - { - final TestEntity entity = TestEntity.builder().complexProperties(new ArrayList<>()).build(); - entity.getComplexProperties().add(TestComplex.builder().stringProperty("Foo").build()); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals( - "{\"ComplexProperties\":[{\"StringProperty\":\"Foo\", \"ComplexProperty\":{}}],\"@odata.type\":\"#TestEntity\"}", - json, - JSONCompareMode.LENIENT); - } - - @Disabled( "Tracking changes within a collection of complex properties is not yet supported. CLOUDECOSYSTEM-8217" ) - // @Test - void testRemoveMemberFromComplexCollection() - throws Exception - { - final TestComplex collectionMember = TestComplex.builder().stringProperty("Foo").build(); - final TestEntity entity = - TestEntity.builder().complexProperties(Collections.singletonList(collectionMember)).build(); - entity.getComplexProperties().remove(collectionMember); - - final String json = new UpdateRequestHelperPatch().toJson(entity, Collections.emptySet()); - assertEquals("{\"ComplexProperties\":[],\"@odata.type\":\"#TestEntity\"}", json, JSONCompareMode.LENIENT); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPutTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPutTest.java deleted file mode 100644 index 3f4762c80c..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/UpdateRequestHelperPutTest.java +++ /dev/null @@ -1,159 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; - -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nonnull; - -import org.junit.jupiter.api.Test; -import org.skyscreamer.jsonassert.JSONCompareMode; - -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.expression.FieldReference; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -class UpdateRequestHelperPutTest -{ - @NoArgsConstructor - @AllArgsConstructor - @Builder - @JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) - private static class TestEntity extends VdmEntity - { - @Getter - private final String odataType = "TestEntity"; - - @Getter - private final String entityCollection = "EntityParentCollection"; - - @Getter - private final Class type = TestEntity.class; - - @Getter - @ElementName( "Value" ) - private BigDecimal value; - - final static SimpleProperty.NumericDecimal VALUE = - new SimpleProperty.NumericDecimal<>(TestEntity.class, "Value"); - - @Getter - @ElementName( "Emails" ) - private List emails; - - final static SimpleProperty.Collection EMAILS = - new SimpleProperty.Collection<>(TestEntity.class, "Emails", String.class); - - @Getter - @ElementName( "Name" ) - private String name; - - final static SimpleProperty.String NAME = new SimpleProperty.String<>(TestEntity.class, "Name"); - - @Getter - @ElementName( "IsRetired" ) - private Boolean isRetired; - - final static SimpleProperty.Boolean IS_RETIRED = - new SimpleProperty.Boolean<>(TestEntity.class, "IsRetired"); - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey key = new ODataEntityKey(ODataProtocol.V4); - key.addKeyProperty("Name", name); - return key; - } - - public void setName( String name ) - { - rememberChangedField("Name", this.name); - this.name = name; - } - - public void setIsRetired( Boolean isRetired ) - { - rememberChangedField("IsRetired", this.isRetired); - this.isRetired = isRetired; - } - - public void setValue( BigDecimal value ) - { - rememberChangedField("Value", this.value); - this.value = value; - } - - public void setEmails( List emails ) - { - rememberChangedField("Emails", this.emails); - this.emails = emails; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map result = new HashMap<>(); - result.put("Name", name); - result.put("Value", value); - result.put("Emails", emails); - result.put("IsRetired", isRetired); - return result; - } - } - - @Test - void testPutPayload() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").isRetired(true).build(); - entity.setValue(new BigDecimal("0.00000000001")); - entity.setEmails(Collections.singletonList("sampleEmail@sap.com")); - final String json = new UpdateRequestHelperPut().toJson(entity, null); - - assertEquals( - "{\"@odata.type\":\"#TestEntity\",\"Value\":0.00000000001,\"Emails\":[\"sampleEmail@sap.com\"],\"Name\":\"Foo\",\"IsRetired\":true}", - json, - JSONCompareMode.LENIENT); - } - - @Test - void testPutPayloadWithExcludedFields() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").isRetired(true).build(); - entity.setValue(new BigDecimal("0.00000000001")); - final List excludedFields = - Arrays.asList(TestEntity.IS_RETIRED, TestEntity.NAME, TestEntity.EMAILS); - final String json = new UpdateRequestHelperPut().toJson(entity, excludedFields); - - assertEquals("{\"@odata.type\":\"#TestEntity\",\"Value\":0.00000000001}", json, JSONCompareMode.LENIENT); - } - - @Test - void testPutPayloadWithNullFields() - throws Exception - { - final TestEntity entity = TestEntity.builder().name("Foo").isRetired(true).build(); - entity.setValue(new BigDecimal("0.00000000001")); - final String json = new UpdateRequestHelperPut().toJson(entity, null); - - assertEquals( - "{\"@odata.type\":\"#TestEntity\",\"Value\":0.00000000001,\"Name\":\"Foo\",\"IsRetired\":true}", - json, - JSONCompareMode.STRICT); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntityTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntityTest.java deleted file mode 100644 index df773b7e3b..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/core/VdmEntityTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.core; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import io.vavr.control.Option; - -class VdmEntityTest -{ - @Test - void testModifications() - { - final TestEntity entity = new TestEntity(); - assertThat(entity).isEqualTo(new TestEntity()); - assertThat(entity.getVersionIdentifier()).isEqualTo(Option.none()); - - entity.setCustomField("foo", "bar"); - - final SimpleProperty customField = () -> "fizz"; - entity.setCustomField(customField, "buzz"); - - assertThat(entity.getCustomFields()).containsEntry("foo", "bar").containsEntry("fizz", "buzz"); - assertThat(entity. getCustomField("foo")).isEqualTo("bar"); - assertThat(entity. getCustomField(customField)).isEqualTo("buzz"); - assertThat(entity.hasCustomField("fizz")).isTrue(); - assertThat(entity.hasCustomField(customField)).isTrue(); - assertThat(entity.getCustomFieldNames()).containsExactly("foo", "fizz"); - - entity.fromMap(Collections.singletonMap("foo", "barbar")); - assertThat(entity. getCustomField("foo")).isEqualTo("barbar"); - - assertThat(entity.toString()).isNotNull(); - } - - @Test - void testChangedNonCustomFields() - { - final TestEntity entity = TestEntity.builder().id("old").build(); - - assertThat(entity.getChangedFields()).isEmpty(); - - entity.setId("old"); - assertThat(entity.getChangedFields()).isEmpty(); - - entity.setId("new"); - assertThat(entity.getChangedFields()).containsOnlyKeys("id"); - } - - @Test - void testChangedCustomFields() - { - final TestEntity entity = TestEntity.builder().id("id").build(); - - assertThat(entity.getChangedFields()).isEmpty(); - - entity.setCustomField("foo", "bar"); - assertThat(entity.getChangedFields()).containsOnlyKeys("foo"); - - entity.resetChangedFields(); - entity.setCustomField("foo", "bar"); - assertThat(entity.getChangedFields()).isEmpty(); - - entity.setCustomField("foo", "baz"); - assertThat(entity.getChangedFields()).containsOnlyKeys("foo"); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableOperandsTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableOperandsTest.java deleted file mode 100644 index e0d40649f3..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/expression/FilterableOperandsTest.java +++ /dev/null @@ -1,575 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.expression; - -import static com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol.V4; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -import java.time.Duration; -import java.time.LocalDate; -import java.util.Arrays; -import java.util.UUID; -import java.util.function.BiFunction; -import java.util.function.Function; - -import org.assertj.core.api.SoftAssertions; -import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odata.client.expression.FilterExpression; -import com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty; -import com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty; -import com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmComplex; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; - -import lombok.Getter; - -class FilterableOperandsTest -{ - private static class TestEntity extends VdmEntity - { - private static final SimpleProperty.Boolean ALIVE = - new SimpleProperty.Boolean<>(TestEntity.class, "Alive"); - private static final SimpleProperty.Enum PROGRAMMING_LANGUAGE = - new SimpleProperty.Enum<>(TestEntity.class, "ProgrammingLanguage", "SoftwareEngineer.Programming.Language"); - - @Getter - private final String entityCollection = "TestEntities"; - @Getter - private final String odataType = "Odata.TestEntity"; - @Getter - private final Class type = TestEntity.class; - - enum Language implements VdmEnum - { - Java; - } - } - - private static class TestComputer extends VdmComplex - { - @Getter - private final String odataType = "Odata.TestComputer"; - @Getter - private final Class type = TestComputer.class; - } - - @Test - void testFilterableBoolean() - { - final SimpleProperty.Boolean f1 = new SimpleProperty.Boolean<>(TestEntity.class, "Field1"); - final String expression = f1.and(true).and(f1).not().or(true).or(f1).getExpression(V4); - assertThat(expression).isEqualTo("(((not ((Field1 and true) and Field1)) or true) or Field1)"); - } - - @Test - void testFilterableCollection() - { - final SoftAssertions softly = new SoftAssertions(); - final SimpleProperty.Collection telephone = - new SimpleProperty.Collection<>(TestEntity.class, "TelephoneNumber", Integer.class); - final SimpleProperty.Collection areaCode = - new SimpleProperty.Collection<>(TestEntity.class, "AreaCode", Integer.class); - final NavigationProperty.Collection friends = - new NavigationProperty.Collection<>(TestEntity.class, "Friends", TestEntity.class); - - { - final String expression = friends.all(TestEntity.ALIVE.equalTo(true)).getExpression(V4); - softly.assertThat(expression).isEqualTo("Friends/all(a:(a/Alive eq true))"); - } - { - final String expression = friends.any(TestEntity.ALIVE.equalTo(true)).getExpression(V4); - softly.assertThat(expression).isEqualTo("Friends/any(a:(a/Alive eq true))"); - } - { - final String expression = areaCode.concat(telephone).concat(Arrays.asList(4, 2)).getExpression(V4); - softly.assertThat(expression).isEqualTo("concat(concat(AreaCode,TelephoneNumber),[4,2])"); - } - { - final String expression = areaCode.substring(1).substring(2, 3).getExpression(V4); - softly.assertThat(expression).isEqualTo("substring(substring(AreaCode,1),2,3)"); - } - { - final String expression = telephone.contains(areaCode).getExpression(V4); - softly.assertThat(expression).isEqualTo("contains(TelephoneNumber,AreaCode)"); - } - { - final String expression = telephone.contains(Arrays.asList(0, 3, 0)).getExpression(V4); - softly.assertThat(expression).isEqualTo("contains(TelephoneNumber,[0,3,0])"); - } - { - final String expression = telephone.endsWith(Arrays.asList(4, 2)).getExpression(V4); - softly.assertThat(expression).isEqualTo("endswith(TelephoneNumber,[4,2])"); - } - { - final String expression = telephone.endsWith(telephone).getExpression(V4); - softly.assertThat(expression).isEqualTo("endswith(TelephoneNumber,TelephoneNumber)"); - } - { - final String expression = telephone.startsWith(Arrays.asList(0, 3, 0)).getExpression(V4); - softly.assertThat(expression).isEqualTo("startswith(TelephoneNumber,[0,3,0])"); - } - { - final String expression = telephone.startsWith(telephone).getExpression(V4); - softly.assertThat(expression).isEqualTo("startswith(TelephoneNumber,TelephoneNumber)"); - } - { - final String expression = telephone.hasSubSequence(areaCode).getExpression(V4); - softly.assertThat(expression).isEqualTo("hassubsequence(TelephoneNumber,AreaCode)"); - } - { - final String expression = telephone.hasSubSequence(Arrays.asList(0, 3, 0)).getExpression(V4); - softly.assertThat(expression).isEqualTo("hassubsequence(TelephoneNumber,[0,3,0])"); - } - { - final String expression = telephone.hasSubset(areaCode).getExpression(V4); - softly.assertThat(expression).isEqualTo("hassubset(TelephoneNumber,AreaCode)"); - } - { - final String expression = telephone.hasSubset(Arrays.asList(0, 3, 0)).getExpression(V4); - softly.assertThat(expression).isEqualTo("hassubset(TelephoneNumber,[0,3,0])"); - } - { - final String expression = telephone.indexOf(areaCode).getExpression(V4); - softly.assertThat(expression).isEqualTo("indexof(TelephoneNumber,AreaCode)"); - } - { - final String expression = telephone.indexOf(Arrays.asList(0, 3, 0)).getExpression(V4); - softly.assertThat(expression).isEqualTo("indexof(TelephoneNumber,[0,3,0])"); - } - { - final String expression = telephone.length().getExpression(V4); - softly.assertThat(expression).isEqualTo("length(TelephoneNumber)"); - } - softly.assertAll(); - } - - @Test - void testFilterableComplex() - { - final ComplexProperty.Single f1 = - new ComplexProperty.Single<>(TestEntity.class, "OperatingSystem", TestComputer.class); - final String expression1 = f1.has("Debian").getExpression(V4); - assertThat(expression1).isEqualTo("(OperatingSystem has 'Debian')"); - - final ComplexProperty.Single f2 = - new ComplexProperty.Single<>(TestEntity.class, "Apartment", TestComputer.class); - final SimpleProperty.Enum enumValue = - new SimpleProperty.Enum<>(TestEntity.class, "Animal", "OData.Test.AnimalType"); - final String expression2 = f2.has(enumValue).getExpression(V4); - assertThat(expression2).isEqualTo("(Apartment has Animal)"); - - assertThat(f1.equalToNull().getExpression(V4)).isEqualTo("(OperatingSystem eq null)"); - assertThat(f1.notEqualToNull().getExpression(V4)).isEqualTo("(OperatingSystem ne null)"); - } - - @Test - void testFilterableTime() - { - final SoftAssertions softly = new SoftAssertions(); - final SimpleProperty.Time time = new SimpleProperty.Time<>(TestEntity.class, "Registration"); - softly.assertThat(time.timeFractionalSeconds().getExpression(V4)).isEqualTo("fractionalseconds(Registration)"); - softly.assertThat(time.timeSecond().getExpression(V4)).isEqualTo("second(Registration)"); - softly.assertThat(time.timeMinute().getExpression(V4)).isEqualTo("minute(Registration)"); - softly.assertThat(time.timeHour().getExpression(V4)).isEqualTo("hour(Registration)"); - softly.assertAll(); - } - - @Test - void testFilterableDate() - { - final SoftAssertions softly = new SoftAssertions(); - final SimpleProperty.Date date = new SimpleProperty.Date<>(TestEntity.class, "Registration"); - final SimpleProperty.Duration duration = - new SimpleProperty.Duration<>(TestEntity.class, "ResponseTime"); - { - final String expression = - date - .add(Duration.ofDays(1)) - .subtract(Duration.ofDays(2)) - .difference(LocalDate.of(2001, 1, 1)) - .getExpression(V4); - softly - .assertThat(expression) - .isEqualTo("(((Registration add duration'PT24H') sub duration'PT48H') sub 2001-01-01)"); - } - { - final String expression = date.add(duration).subtract(duration).difference(date).getExpression(V4); - softly - .assertThat(expression) - .isEqualTo("(((Registration add ResponseTime) sub ResponseTime) sub Registration)"); - } - softly.assertThat(date.dateDay().getExpression(V4)).isEqualTo("day(Registration)"); - softly.assertThat(date.dateMonth().getExpression(V4)).isEqualTo("month(Registration)"); - softly.assertThat(date.dateYear().getExpression(V4)).isEqualTo("year(Registration)"); - softly.assertAll(); - } - - @Test - void testFilterableDateTime() - { - final SoftAssertions softly = new SoftAssertions(); - final SimpleProperty.DateTime dt = new SimpleProperty.DateTime<>(TestEntity.class, "Registration"); - final SimpleProperty.Duration duration = - new SimpleProperty.Duration<>(TestEntity.class, "ResponseTime"); - - { - final String expression = dt.add(Duration.ofDays(1)).subtract(Duration.ofDays(2)).getExpression(V4); - softly.assertThat(expression).isEqualTo("((Registration add duration'PT24H') sub duration'PT48H')"); - } - { - final String expression = dt.add(duration).subtract(duration).getExpression(V4); - softly.assertThat(expression).isEqualTo("((Registration add ResponseTime) sub ResponseTime)"); - } - softly.assertThat(dt.date().getExpression(V4)).isEqualTo("date(Registration)"); - softly.assertThat(dt.time().getExpression(V4)).isEqualTo("time(Registration)"); - softly.assertThat(dt.timeFractionalSeconds().getExpression(V4)).isEqualTo("fractionalseconds(Registration)"); - softly.assertThat(dt.timeHour().getExpression(V4)).isEqualTo("hour(Registration)"); - softly.assertThat(dt.timeMinute().getExpression(V4)).isEqualTo("minute(Registration)"); - softly.assertThat(dt.timeSecond().getExpression(V4)).isEqualTo("second(Registration)"); - softly.assertThat(dt.dateDay().getExpression(V4)).isEqualTo("day(Registration)"); - softly.assertThat(dt.dateMonth().getExpression(V4)).isEqualTo("month(Registration)"); - softly.assertThat(dt.dateYear().getExpression(V4)).isEqualTo("year(Registration)"); - softly.assertThat(dt.offsetMinutes().getExpression(V4)).isEqualTo("totaloffsetminutes(Registration)"); - softly.assertAll(); - } - - @Test - void testFilterableDuration() - { - final SoftAssertions softly = new SoftAssertions(); - final SimpleProperty.Duration duration = - new SimpleProperty.Duration<>(TestEntity.class, "ResponseTime"); - final SimpleProperty.NumericDecimal number = - new SimpleProperty.NumericDecimal<>(TestEntity.class, "Size"); - { - final String expression = duration.add(duration).subtract(duration).negate().getExpression(V4); - softly.assertThat(expression).isEqualTo("-(((ResponseTime add ResponseTime) sub ResponseTime))"); - } - { - final String expression = duration.add(Duration.ofDays(1)).subtract(Duration.ofDays(2)).getExpression(V4); - softly.assertThat(expression).isEqualTo("((ResponseTime add duration'PT24H') sub duration'PT48H')"); - } - { - final String expression = duration.divide(2).multiply(3).getExpression(V4); - softly.assertThat(expression).isEqualTo("((ResponseTime div 2) mul 3)"); - } - { - final String expression = duration.divide(number).multiply(number).getExpression(V4); - softly.assertThat(expression).isEqualTo("((ResponseTime div Size) mul Size)"); - } - softly.assertThat(duration.offsetSeconds().getExpression(V4)).isEqualTo("totaloffsetseconds(ResponseTime)"); - softly.assertAll(); - } - - @Test - void testFilterableNumberApiInteger() - { - final SimpleProperty.NumericInteger number = - new SimpleProperty.NumericInteger<>(TestEntity.class, "Age"); - final SoftAssertions softly = new SoftAssertions(); - - // internal API tests - { - final FilterExpression delegate = mock(FilterExpression.class); - final FilterableNumericInteger.Expression expression = - new FilterableNumericInteger.Expression<>(delegate, Object.class); - softly.assertThat(expression.getDelegate()).isEqualTo(delegate); - softly.assertThat(expression.getEntityType()).isEqualTo(Object.class); - } - - // type safety for NumericInteger functions - Lists - ., FilterableNumeric>> newArrayList(FilterableNumericInteger::negate - - ) - .forEach(f -> softly.assertThat(f.apply(number)).isInstanceOf(FilterableNumericInteger.class)); - - // type safety for NumericInteger functions and Long argument - Lists - ., Long, FilterableNumeric>> newArrayList( - FilterableNumericInteger::add, - FilterableNumericInteger::subtract, - FilterableNumericInteger::multiply, - FilterableNumericInteger::modulo) - .forEach( - f -> softly - .assertThat(f.apply(number, 42L)) - .describedAs(f.toString()) - .isInstanceOf(FilterableNumericInteger.class)); - - // type safety for NumericInteger functions and Integer argument - Lists - ., Integer, FilterableNumeric>> newArrayList( - FilterableNumericInteger::add, - FilterableNumericInteger::subtract, - FilterableNumericInteger::multiply, - FilterableNumericInteger::modulo) - .forEach( - f -> softly - .assertThat(f.apply(number, 42)) - .describedAs(f.toString()) - .isInstanceOf(FilterableNumericInteger.class)); - - // type safety for NumericInteger functions and Decimal argument - Lists - ., Number, FilterableNumeric>> newArrayList( - FilterableNumericInteger::add, - FilterableNumericInteger::subtract, - FilterableNumericInteger::multiply, - FilterableNumericInteger::divide, - FilterableNumericInteger::modulo) - .forEach( - f -> softly - .assertThat(f.apply(number, Math.PI)) - .describedAs(f.toString()) - .isInstanceOf(FilterableNumericDecimal.class)); - - softly.assertAll(); - } - - @Test - void testFilterableNumberApiDecimal() - { - final SimpleProperty.NumericDecimal number = - new SimpleProperty.NumericDecimal<>(TestEntity.class, "Size"); - final SoftAssertions softly = new SoftAssertions(); - - // internal API tests - { - final FilterExpression delegate = mock(FilterExpression.class); - final FilterableNumericDecimal.Expression expression = - new FilterableNumericDecimal.Expression<>(delegate, Object.class); - softly.assertThat(expression.getDelegate()).isEqualTo(delegate); - softly.assertThat(expression.getEntityType()).isEqualTo(Object.class); - } - - // type safety for NumericDecimal functions - Lists - ., FilterableNumeric>> newArrayList(FilterableNumericDecimal::negate - - ) - .forEach(f -> softly.assertThat(f.apply(number)).isInstanceOf(FilterableNumericDecimal.class)); - - // type safety for NumericDecimal functions - Lists - ., FilterableNumeric>> newArrayList( - FilterableNumericDecimal::ceil, - FilterableNumericDecimal::floor, - FilterableNumericDecimal::round - - ) - .forEach(f -> softly.assertThat(f.apply(number)).isInstanceOf(FilterableNumericInteger.class)); - - // type safety for NumericDecimal functions and Long argument - Lists - ., Long, FilterableNumeric>> newArrayList( - FilterableNumericDecimal::add, - FilterableNumericDecimal::subtract, - FilterableNumericDecimal::multiply, - FilterableNumericDecimal::modulo) - .forEach( - f -> softly - .assertThat(f.apply(number, 42L)) - .describedAs(f.toString()) - .isInstanceOf(FilterableNumericDecimal.class)); - - // type safety for NumericDecimal functions and Integer argument - Lists - ., Integer, FilterableNumeric>> newArrayList( - FilterableNumericDecimal::add, - FilterableNumericDecimal::subtract, - FilterableNumericDecimal::multiply, - FilterableNumericDecimal::modulo) - .forEach( - f -> softly - .assertThat(f.apply(number, 42)) - .describedAs(f.toString()) - .isInstanceOf(FilterableNumericDecimal.class)); - - // type safety for NumericDecimal functions and Decimal argument - Lists - ., Number, FilterableNumeric>> newArrayList( - FilterableNumericDecimal::add, - FilterableNumericDecimal::subtract, - FilterableNumericDecimal::multiply, - FilterableNumericDecimal::divide, - FilterableNumericDecimal::modulo) - .forEach( - f -> softly - .assertThat(f.apply(number, Math.PI)) - .describedAs(f.toString()) - .isInstanceOf(FilterableNumericDecimal.class)); - - softly.assertAll(); - } - - @Test - void testFilterableNumber() - { - final SoftAssertions softly = new SoftAssertions(); - final SimpleProperty.NumericInteger number = - new SimpleProperty.NumericInteger<>(TestEntity.class, "Age"); - final SimpleProperty.NumericDecimal size = - new SimpleProperty.NumericDecimal<>(TestEntity.class, "Size"); - { - final String expression = number.add(number).subtract(number).negate().getExpression(V4); - softly.assertThat(expression).isEqualTo("-(((Age add Age) sub Age))"); - } - { - final String expression = size.add(size).subtract(size).negate().getExpression(V4); - softly.assertThat(expression).isEqualTo("-(((Size add Size) sub Size))"); - } - { - final String expression = number.add(1).subtract(2).getExpression(V4); - softly.assertThat(expression).isEqualTo("((Age add 1) sub 2)"); - } - { - final String expression = number.multiply(number).divide(number).getExpression(V4); - softly.assertThat(expression).isEqualTo("((Age mul Age) divby Age)"); - } - { - final String expression = size.multiply(size).divide(size).getExpression(V4); - softly.assertThat(expression).isEqualTo("((Size mul Size) divby Size)"); - } - { - final String expression = number.multiply(1).divide(2).getExpression(V4); - softly.assertThat(expression).isEqualTo("((Age mul 1) divby 2)"); - } - { - final String expression = number.modulo(number).modulo(3).getExpression(V4); - softly.assertThat(expression).isEqualTo("((Age mod Age) mod 3)"); - } - { - final String expression = size.modulo(size).modulo(3.0).getExpression(V4); - softly.assertThat(expression).isEqualTo("((Size mod Size) mod 3.0)"); - } - { - final String expression = size.in(42, 13.37, 9000f).getExpression(V4); - softly.assertThat(expression).isEqualTo("(Size in (42,13.37,9000.0))"); - } - { - softly.assertThat(size.ceil().getExpression(V4)).isEqualTo("ceiling(Size)"); - softly.assertThat(size.floor().getExpression(V4)).isEqualTo("floor(Size)"); - softly.assertThat(size.round().getExpression(V4)).isEqualTo("round(Size)"); - } - { - final String expression = - number - .equalTo(number) - .and(number.equalTo(42)) - .and(number.notEqualTo(number)) - .and(number.notEqualTo(42)) - .getExpression(V4); - softly - .assertThat(expression) - .isEqualTo("((((Age eq Age) and (Age eq 42)) and (Age ne Age)) and (Age ne 42))"); - } - { - final String expression = - number - .greaterThan(number) - .and(number.greaterThan(42)) - .and(number.greaterThanEqual(number)) - .and(number.greaterThanEqual(42)) - .getExpression(V4); - softly - .assertThat(expression) - .isEqualTo("((((Age gt Age) and (Age gt 42)) and (Age ge Age)) and (Age ge 42))"); - } - { - final String expression = - number - .lessThan(number) - .and(number.lessThan(42)) - .and(number.lessThanEqual(number)) - .and(number.lessThanEqual(42)) - .getExpression(V4); - softly - .assertThat(expression) - .isEqualTo("((((Age lt Age) and (Age lt 42)) and (Age le Age)) and (Age le 42))"); - } - softly.assertAll(); - } - - @Test - void testGuid() - { - final SimpleProperty.Guid id = new SimpleProperty.Guid<>(TestEntity.class, "Id"); - assertThat(id.equalTo(UUID.fromString("b3e130fe-d72c-4a5b-8dcf-463b497f985c")).getExpression(V4)) - .isEqualTo("(Id eq b3e130fe-d72c-4a5b-8dcf-463b497f985c)"); - } - - @Test - void testFilterableString() - { - final SoftAssertions softly = new SoftAssertions(); - final SimpleProperty.String name = new SimpleProperty.String<>(TestEntity.class, "Name"); - - softly.assertThat(name.equalTo((String) null).getExpression(V4)).isEqualTo("(Name eq null)"); - softly.assertThat(name.equalToNull().getExpression(V4)).isEqualTo("(Name eq null)"); - softly.assertThat(name.equalToNull().not().getExpression(V4)).isEqualTo("(not (Name eq null))"); - softly.assertThat(name.notEqualToNull().getExpression(V4)).isEqualTo("(Name ne null)"); - softly.assertThat(name.indexOf(name).getExpression(V4)).isEqualTo("indexof(Name,Name)"); - softly.assertThat(name.indexOf("Foo").getExpression(V4)).isEqualTo("indexof(Name,'Foo')"); - softly.assertThat(name.length().getExpression(V4)).isEqualTo("length(Name)"); - softly.assertThat(name.matches("Foo").getExpression(V4)).isEqualTo("matchesPattern(Name,'Foo')"); - softly.assertThat(name.toUpper().toLower().getExpression(V4)).isEqualTo("tolower(toupper(Name))"); - - softly - .assertThat(name.concat(name).concat("Foo").trim().getExpression(V4)) - .isEqualTo("trim(concat(concat(Name,Name),'Foo'))"); - - softly - .assertThat(name.substring(1).substring(2, 3).getExpression(V4)) - .isEqualTo("substring(substring(Name,1),2,3)"); - - softly - .assertThat(name.startsWith(name).and(name.startsWith("Foo")).getExpression(V4)) - .isEqualTo("(startswith(Name,Name) and startswith(Name,'Foo'))"); - - softly - .assertThat(name.endsWith(name).and(name.endsWith("Foo")).getExpression(V4)) - .isEqualTo("(endswith(Name,Name) and endswith(Name,'Foo'))"); - - softly - .assertThat(name.contains("Foo").and(name.contains(name)).getExpression(V4)) - .isEqualTo("(contains(Name,'Foo') and contains(Name,Name))"); - - softly.assertThat(name.equalTo("Qu'te").getExpression(V4)).isEqualTo("(Name eq 'Qu''te')"); - - softly.assertAll(); - } - - @Test - void testFilterableEnum() - { - final SoftAssertions softly = new SoftAssertions(); - softly - .assertThat(TestEntity.PROGRAMMING_LANGUAGE.equalToNull().getExpression(V4)) - .isEqualTo("(ProgrammingLanguage eq null)"); - softly - .assertThat(TestEntity.PROGRAMMING_LANGUAGE.notEqualToNull().getExpression(V4)) - .isEqualTo("(ProgrammingLanguage ne null)"); - softly - .assertThat(TestEntity.PROGRAMMING_LANGUAGE.equalTo((TestEntity.Language) null).getExpression(V4)) - .isEqualTo("(ProgrammingLanguage eq null)"); - softly - .assertThat(TestEntity.PROGRAMMING_LANGUAGE.notEqualTo((TestEntity.Language) null).getExpression(V4)) - .isEqualTo("(ProgrammingLanguage ne null)"); - softly - .assertThat(TestEntity.PROGRAMMING_LANGUAGE.equalTo(TestEntity.Language.Java).getExpression(V4)) - .isEqualTo("(ProgrammingLanguage eq SoftwareEngineer.Programming.Language'Java')"); - softly - .assertThat(TestEntity.PROGRAMMING_LANGUAGE.notEqualTo(TestEntity.Language.Java).getExpression(V4)) - .isEqualTo("(ProgrammingLanguage ne SoftwareEngineer.Programming.Language'Java')"); - softly - .assertThat(TestEntity.PROGRAMMING_LANGUAGE.equalTo(TestEntity.PROGRAMMING_LANGUAGE).getExpression(V4)) - .isEqualTo("(ProgrammingLanguage eq ProgrammingLanguage)"); - softly - .assertThat(TestEntity.PROGRAMMING_LANGUAGE.notEqualTo(TestEntity.PROGRAMMING_LANGUAGE).getExpression(V4)) - .isEqualTo("(ProgrammingLanguage ne ProgrammingLanguage)"); - softly.assertAll(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/BooleanQueriesTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/BooleanQueriesTest.java deleted file mode 100644 index 35f2b2e9b4..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/BooleanQueriesTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class BooleanQueriesTest -{ - @Test - void testGetFilteredWithNot() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.USER_NAME.length().greaterThanEqual(8).not()); - final String expectedUnencodedFilter = "(not (length(UserName) ge 8))"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithAnd() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .filter(Person.FIRST_NAME.length().lessThanEqual(8).and(Person.LAST_NAME.length().lessThanEqual(8))); - final String expectedUnencodedFilter = "((length(FirstName) le 8) and (length(LastName) le 8))"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithOr() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .filter(Person.FIRST_NAME.length().lessThanEqual(8).or(Person.LAST_NAME.length().lessThanEqual(8))); - final String expectedUnencodedFilter = "((length(FirstName) le 8) or (length(LastName) le 8))"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/CollectionQueriesTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/CollectionQueriesTest.java deleted file mode 100644 index ff4c7f4216..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/CollectionQueriesTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.PersonGender; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class CollectionQueriesTest -{ - @Test - void testGetFilteredWithAny() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .filter(Person.TO_FRIENDS.any(Person.FIRST_NAME.equalTo("Angel"))); - final String expectedUnencodedFilter = "Friends/any(a:(a/FirstName eq 'Angel'))"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithAll() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .filter(Person.TO_FRIENDS.all(Person.FIRST_NAME.equalTo("Angel"))); - final String expectedUnencodedFilter = "Friends/all(a:(a/FirstName eq 'Angel'))"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testEnumFilter() - { - - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .filter(Person.TO_FRIENDS.all(Person.GENDER.notEqualTo(PersonGender.MALE))); - - final String expectedUnencodedFilter = "Friends/all(a:(a/Gender ne Trippin.PersonGender'Male'))"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ComparisonQueriesTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ComparisonQueriesTest.java deleted file mode 100644 index cdaa55102d..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ComparisonQueriesTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class ComparisonQueriesTest -{ - @Test - void testGetFilteredWithEquals() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.TO_TRIPS.length().equalTo(5)); - final String expectedUnencodedFilter = "(length(Trips) eq 5)"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithNotEquals() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.TO_TRIPS.length().notEqualTo(10)); - final String expectedUnencodedFilter = "(length(Trips) ne 10)"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithLessThan() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.TO_TRIPS.length().lessThan(2)); - final String expectedUnencodedFilter = "(length(Trips) lt 2)"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithGreaterThan() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.TO_TRIPS.length().greaterThan(15)); - final String expectedUnencodedFilter = "(length(Trips) gt 15)"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithLessThanOrEquals() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.TO_TRIPS.length().lessThanEqual(1)); - final String expectedUnencodedFilter = "(length(Trips) le 1)"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithGreaterThanOrEquals() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.TO_TRIPS.length().greaterThanEqual(20)); - final String expectedUnencodedFilter = "(length(Trips) ge 20)"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithInLiterals() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.USER_NAME.in("scottketchum", "javieralfred")); - final String expectedUnencodedFilter = "(UserName in ('scottketchum','javieralfred'))"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithInSimpleCollection() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.USER_NAME.in(Person.EMAILS)); - final String expectedUnencodedFilter = "(UserName in Emails)"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithInNavigationPropertyCollection() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.TO_BEST_FRIEND.in(Person.TO_FRIENDS)); - final String expectedUnencodedFilter = "(BestFriend in Friends)"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } - - @Test - void testGetFilteredWithInEnumCollection() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().filter(Person.FAVORITE_FEATURE.in(Person.FEATURES)); - final String expectedUnencodedFilter = "(FavoriteFeature in Features)"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$filter", expectedUnencodedFilter); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/CountEntityUnitTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/CountEntityUnitTest.java deleted file mode 100644 index a502c39147..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/CountEntityUnitTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; - -import javax.annotation.Nonnull; - -import org.apache.hc.core5.http.ContentType; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.google.gson.JsonSyntaxException; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataDeserializationException; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.TrippinService; - -@WireMockTest -class CountEntityUnitTest -{ - private static final WireMockConfiguration WIREMOCK_CONFIGURATION = wireMockConfig().dynamicPort(); - - private DefaultHttpDestination destination; - private TrippinService service; - - private static final String COUNT_REQUEST_URL = - String.format("%s/%s/$count", TrippinService.DEFAULT_SERVICE_PATH, "People"); - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - service = new DefaultTrippinService().withServicePath(TrippinService.DEFAULT_SERVICE_PATH); - } - - @Test - void testSuccessfulCount() - { - stubFor( - get(urlPathEqualTo(COUNT_REQUEST_URL)) - .withQueryParam("$filter", equalTo("contains(FirstName,'Bar')")) - .withQueryParam("$search", equalTo("\"Foo\"")) - .willReturn(okForContentType(ContentType.TEXT_PLAIN.getMimeType(), "42"))); - - final Long countResponse = - service.countPeople().search("Foo").filter(Person.FIRST_NAME.contains("Bar")).execute(destination); - - verify(1, getRequestedFor(urlPathEqualTo(COUNT_REQUEST_URL))); - - assertThat(countResponse).isEqualTo(42L); - } - - @Test - void testFailCountNull() - { - stubFor( - get(urlPathEqualTo(COUNT_REQUEST_URL)) - .willReturn(okForContentType(ContentType.TEXT_PLAIN.getMimeType(), "null"))); - - assertThatCode(() -> service.countPeople().execute(destination)) - .isInstanceOf(ODataDeserializationException.class) - .matches(e -> e.getCause().getMessage().contains("null")); - } - - @Test - void testFailCountEmpty() - { - stubFor( - get(urlPathEqualTo(COUNT_REQUEST_URL)) - .willReturn(okForContentType(ContentType.TEXT_PLAIN.getMimeType(), ""))); - - assertThatCode(() -> service.countPeople().execute(destination)) - .isInstanceOf(ODataDeserializationException.class) - .matches(e -> e.getCause().getMessage().contains("null")); - } - - @Test - void testFailCountJson() - { - stubFor(get(urlPathEqualTo(COUNT_REQUEST_URL)).willReturn(okJson("{\"foo\":\"bar\"}"))); - assertThatCode(() -> service.countPeople().execute(destination)) - .isInstanceOf(ODataDeserializationException.class); - } - - @Test - void testFailCountNoInteger() - { - stubFor( - get(urlPathEqualTo(COUNT_REQUEST_URL)) - .willReturn(okForContentType(ContentType.TEXT_PLAIN.getMimeType(), "a"))); - - assertThatCode(() -> service.countPeople().execute(destination)) - .isInstanceOf(ODataDeserializationException.class) - .hasCauseInstanceOf(JsonSyntaxException.class) - .hasRootCauseExactlyInstanceOf(NumberFormatException.class); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ETagTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ETagTest.java deleted file mode 100644 index 1b1f60844e..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ETagTest.java +++ /dev/null @@ -1,375 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static com.github.tomakehurst.wiremock.client.WireMock.any; -import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.delete; -import static com.github.tomakehurst.wiremock.client.WireMock.deleteRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.noContent; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okJson; -import static com.github.tomakehurst.wiremock.client.WireMock.patch; -import static com.github.tomakehurst.wiremock.client.WireMock.patchRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.put; -import static com.github.tomakehurst.wiremock.client.WireMock.putRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.List; - -import javax.annotation.Nonnull; - -import org.apache.hc.core5.http.HttpHeaders; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odatav4.TestUtility; -import com.sap.cloud.sdk.datamodel.odatav4.core.ModificationResponse; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.TrippinService; - -/** - * Tests the proper submission of the entity version identifier, transmitted as ETag in the HTTP headers. - */ -@WireMockTest -class ETagTest -{ - private static final String SERVICE_URL = "/some/service"; - private static final String ENTITY_COLLECTION = "People"; - private static final String GET_ALL_REQUEST_URL = SERVICE_URL + "/" + ENTITY_COLLECTION; - private static final String CREATE_URL = SERVICE_URL + "/" + ENTITY_COLLECTION; - private static final String GET_BY_KEY_REQUEST_URL = SERVICE_URL + "/" + ENTITY_COLLECTION + "('russellwhyte')"; - - private static final String GET_ALL_RESPONSE_BODY = readResourceFile("GetAllResponseBody.json"); - private static final String GET_BY_KEY_RESPONSE_BODY = readResourceFile("GetSingleResponseBody.json"); - - private static final String ETAG_RUSSELL = "W/\"123\""; - // the jetty under wiremock appends a '--gzip' to the ETag, therefore we need to check this adjusted ETag - private static final String ETAG_RUSSELL_GZIPPED = "W/\"123--gzip\""; - private static final String ETAG_RUSSELL_UPDATED = "W/\"456\""; - private static final String ETAG_SCOTT = "W/\"999\""; - - private static final String X_CSRF_TOKEN_HEADER_KEY = "x-csrf-token"; - private static final String X_CSRF_TOKEN_HEADER_FETCH_VALUE = "fetch"; - private static final String X_CSRF_TOKEN_HEADER_VALUE = "awesome-csrf-token"; - - private static final WireMockConfiguration WIREMOCK_CONFIGURATION = wireMockConfig().dynamicPort(); - - private DefaultHttpDestination destination; - private final TrippinService service = new DefaultTrippinService().withServicePath(SERVICE_URL); - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - - tellWiremockToReturnCsrfToken(); - } - - private void tellWiremockToReturnCsrfToken() - { - stubFor( - head(urlEqualTo(SERVICE_URL)) - .withHeader(X_CSRF_TOKEN_HEADER_KEY, equalTo(X_CSRF_TOKEN_HEADER_FETCH_VALUE)) - .willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, X_CSRF_TOKEN_HEADER_VALUE))); - } - - private static String readResourceFile( final String resourceFileName ) - { - return TestUtility.readResourceFile(ETagTest.class, resourceFileName); - } - - @Test - void testParseETagFromGetAllResponse() - { - stubFor(get(urlEqualTo(GET_ALL_REQUEST_URL)).willReturn(okJson(GET_ALL_RESPONSE_BODY))); - - final List result = service.getAllPeople().execute(destination); - assertThat(result.get(0).getVersionIdentifier().get()).isEqualTo(ETAG_RUSSELL); - assertThat(result.get(1).getVersionIdentifier().get()).isEqualTo(ETAG_SCOTT); - } - - @Test - void testParseETagFromCreateResponse() - { - stubFor( - post(urlEqualTo(CREATE_URL)).willReturn(okJson(GET_BY_KEY_RESPONSE_BODY).withHeader("ETag", ETAG_RUSSELL))); - final Person person = Person.builder().firstName("Russel").build(); - final ModificationResponse result = service.createPeople(person).execute(destination); - - assertThat( - result - .getModifiedEntity() - .getVersionIdentifier() - .getOrElseThrow( - () -> new AssertionError("Expected version identifier to be present on Create response."))) - .isEqualTo(ETAG_RUSSELL_GZIPPED); - } - - @Test - void testParseETagFromUpdateResponse() - { - stubFor( - patch(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .willReturn(okJson(GET_BY_KEY_RESPONSE_BODY).withHeader("ETag", ETAG_RUSSELL))); - final Person person = Person.builder().userName("russellwhyte").build(); - final ModificationResponse result = service.updatePeople(person).execute(destination); - - assertThat( - result - .getModifiedEntity() - .getVersionIdentifier() - .getOrElseThrow( - () -> new AssertionError("Expected version identifier to be present on Update response."))) - .isEqualTo(ETAG_RUSSELL_GZIPPED); - } - - @Test - void testParseETagFromGetByKeyResponse() - { - stubFor( - get(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .willReturn(okJson(GET_BY_KEY_RESPONSE_BODY).withHeader("ETag", ETAG_RUSSELL))); - - final Person russel = service.getPeopleByKey("russellwhyte").execute(destination); - - assertThat( - russel - .getVersionIdentifier() - .getOrElseThrow( - () -> new AssertionError("Expected version identifier to be present on GetByKey response."))) - .isEqualTo(ETAG_RUSSELL_GZIPPED); - } - - @Test - void testUpdateWithPATCHContainsEtagByDefault() - { - stubFor( - patch(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .withHeader(HttpHeaders.IF_MATCH, equalTo(ETAG_RUSSELL)) - .willReturn(noContent().withHeader("ETag", ETAG_RUSSELL_UPDATED))); - - final Person russel = new Person(); - russel.setUserName("russellwhyte"); - russel.setVersionIdentifier(ETAG_RUSSELL); - - final ModificationResponse result = service.updatePeople(russel).modifyingEntity().execute(destination); - - assertThat(russel.getVersionIdentifier()).containsExactly(ETAG_RUSSELL); // origin entity remains unchanged - assertThat(result.getUpdatedVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - - verify( - patchRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .withHeader(HttpHeaders.IF_MATCH, equalTo(ETAG_RUSSELL))); - } - - @Test - void testUpdateWithPATCHLacksETagIfDisabled() - { - stubFor( - patch(urlEqualTo(GET_BY_KEY_REQUEST_URL)).willReturn(noContent().withHeader("ETag", ETAG_RUSSELL_UPDATED))); - - final Person russel = new Person(); - russel.setUserName("russellwhyte"); - russel.setVersionIdentifier(ETAG_RUSSELL); - - final ModificationResponse result = - service.updatePeople(russel).modifyingEntity().disableVersionIdentifier().execute(destination); - - assertThat(russel.getVersionIdentifier()).containsExactly(ETAG_RUSSELL); // origin entity remains unchanged - assertThat(result.getUpdatedVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - - verify(patchRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)).withoutHeader(HttpHeaders.IF_MATCH)); - } - - @Test - void testUpdateWithPATCHMatchesAnyETagIfChosen() - { - stubFor( - patch(urlEqualTo(GET_BY_KEY_REQUEST_URL)).willReturn(noContent().withHeader("ETag", ETAG_RUSSELL_UPDATED))); - - final Person russel = new Person(); - russel.setUserName("russellwhyte"); - russel.setVersionIdentifier(ETAG_RUSSELL); - - final ModificationResponse result = - service.updatePeople(russel).modifyingEntity().matchAnyVersionIdentifier().execute(destination); - - assertThat(russel.getVersionIdentifier()).containsExactly(ETAG_RUSSELL); // origin entity remains unchanged - assertThat(result.getUpdatedVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - - verify(patchRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)).withHeader(HttpHeaders.IF_MATCH, equalTo("*"))); - } - - @Test - void testUpdateWithPUTContainsETagByDefault() - { - stubFor( - put(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .withHeader(HttpHeaders.IF_MATCH, equalTo(ETAG_RUSSELL)) - .willReturn(noContent().withHeader("ETag", ETAG_RUSSELL_UPDATED))); - - final Person russel = new Person(); - russel.setUserName("russellwhyte"); - russel.setVersionIdentifier(ETAG_RUSSELL); - - final ModificationResponse result = service.updatePeople(russel).replacingEntity().execute(destination); - - assertThat(russel.getVersionIdentifier()).containsExactly(ETAG_RUSSELL); // origin entity remains unchanged - assertThat(result.getUpdatedVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - - verify( - putRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .withHeader(HttpHeaders.IF_MATCH, equalTo(ETAG_RUSSELL))); - } - - @Test - void testUpdateWithPUTLacksETagIfDisabled() - { - stubFor( - put(urlEqualTo(GET_BY_KEY_REQUEST_URL)).willReturn(noContent().withHeader("ETag", ETAG_RUSSELL_UPDATED))); - - final Person russel = new Person(); - russel.setUserName("russellwhyte"); - russel.setVersionIdentifier(ETAG_RUSSELL); - - final ModificationResponse result = - service.updatePeople(russel).replacingEntity().disableVersionIdentifier().execute(destination); - - assertThat(russel.getVersionIdentifier()).containsExactly(ETAG_RUSSELL); // origin entity remains unchanged - assertThat(result.getUpdatedVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - - verify(putRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)).withoutHeader(HttpHeaders.IF_MATCH)); - } - - @Test - void testUpdateWithPUTLacksMatchesAnyETagIfChosen() - { - stubFor( - put(urlEqualTo(GET_BY_KEY_REQUEST_URL)).willReturn(noContent().withHeader("ETag", ETAG_RUSSELL_UPDATED))); - - final Person russel = new Person(); - russel.setUserName("russellwhyte"); - russel.setVersionIdentifier(ETAG_RUSSELL); - - final ModificationResponse result = - service.updatePeople(russel).replacingEntity().matchAnyVersionIdentifier().execute(destination); - - assertThat(russel.getVersionIdentifier()).containsExactly(ETAG_RUSSELL); // origin entity remains unchanged - assertThat(result.getUpdatedVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - assertThat(result.getModifiedEntity().getVersionIdentifier()).containsExactly(ETAG_RUSSELL_UPDATED); - - verify(putRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)).withHeader(HttpHeaders.IF_MATCH, equalTo("*"))); - } - - @Test - void testDeleteContainsETagByDefault() - { - stubFor( - delete(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .withHeader(HttpHeaders.IF_MATCH, equalTo(ETAG_RUSSELL)) - .willReturn(noContent())); - - final Person russel = new Person(); - russel.setUserName("russellwhyte"); - russel.setVersionIdentifier(ETAG_RUSSELL); - - service.deletePeople(russel).execute(destination); - - verify( - deleteRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .withHeader(HttpHeaders.IF_MATCH, equalTo(ETAG_RUSSELL))); - } - - @Test - void testDeleteLacksEtagIfDisabled() - { - stubFor(delete(urlEqualTo(GET_BY_KEY_REQUEST_URL)).willReturn(noContent())); - - final Person russel = new Person(); - russel.setUserName("russellwhyte"); - russel.setVersionIdentifier(ETAG_RUSSELL); - - service.deletePeople(russel).disableVersionIdentifier().execute(destination); - - verify(deleteRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)).withoutHeader(HttpHeaders.IF_MATCH)); - } - - @Test - void testDeleteMatchesAnyETagIfChosen() - { - stubFor(delete(urlEqualTo(GET_BY_KEY_REQUEST_URL)).willReturn(noContent())); - - final Person russel = new Person(); - russel.setUserName("russellwhyte"); - russel.setVersionIdentifier(ETAG_RUSSELL); - - service.deletePeople(russel).matchAnyVersionIdentifier().execute(destination); - - verify(deleteRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)).withHeader(HttpHeaders.IF_MATCH, equalTo("*"))); - } - - @Test - void testNoETagReceived() - { - stubFor(get(urlEqualTo(GET_BY_KEY_REQUEST_URL)).willReturn(okJson(GET_BY_KEY_RESPONSE_BODY))); - final Person person = service.getPeopleByKey("russellwhyte").execute(destination); - - assertThat(person.getVersionIdentifier()).isEmpty(); - } - - @Test - void testNoIfMatchIsSentOnEmptyETag() - { - stubFor(any(urlEqualTo(GET_BY_KEY_REQUEST_URL)).willReturn(noContent())); - - final Person person = new Person(); - person.setUserName("russellwhyte"); - - service.updatePeople(person).execute(destination); - service.deletePeople(person).execute(destination); - - verify(2, anyRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)).withoutHeader(HttpHeaders.IF_MATCH)); - } - - @Test - void testOverwritingExistingETagOnEntity() - { - stubFor( - any(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .withHeader(HttpHeaders.IF_MATCH, equalTo(ETAG_RUSSELL_UPDATED)) - .willReturn(noContent())); - - final Person person = new Person(); - person.setUserName("russellwhyte"); - person.setVersionIdentifier(ETAG_RUSSELL); - - service.deletePeople(person).withHeader(HttpHeaders.IF_MATCH, ETAG_RUSSELL_UPDATED).execute(destination); - service - .updatePeople(person) - .modifyingEntity() - .withHeader(HttpHeaders.IF_MATCH, ETAG_RUSSELL_UPDATED) - .execute(destination); - - verify( - 2, - anyRequestedFor(urlEqualTo(GET_BY_KEY_REQUEST_URL)) - .withHeader(HttpHeaders.IF_MATCH, equalTo(ETAG_RUSSELL_UPDATED))); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/EncodedParameterTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/EncodedParameterTest.java deleted file mode 100644 index dad74c4170..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/EncodedParameterTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.core.GetByKeyRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class EncodedParameterTest -{ - @Test - void testGetByKeyWithSpecialCharacters() - { - final String fullQueryWithSpecialCharacters = - DefaultTrippinService.DEFAULT_SERVICE_PATH + "/People('" + "test%2F%3F%20%23&user''%25$" + "')"; - final GetByKeyRequestBuilder request = new DefaultTrippinService().getPeopleByKey("test/? #&user'%$"); - assertThat(request.toRequest().getRelativeUri()).hasToString(fullQueryWithSpecialCharacters); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/EncodedQueryTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/EncodedQueryTest.java deleted file mode 100644 index bba02668a9..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/EncodedQueryTest.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Trip; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class EncodedQueryTest -{ - @Test - void testEncodingInFilter() - { - final ODataRequestRead query = - new DefaultTrippinService() - .getAllPeople() - .filter( - Person.USER_NAME - .length() - .greaterThanEqual(8) - .not() - .and(Person.FIRST_NAME.length().greaterThanEqual(10)) - .or(Person.LAST_NAME.length().lessThanEqual(7))) - .toRequest(); - - final String expectedEncodedQuery = - "$filter=(((not%20(length(UserName)%20ge%208))%20and%20(length(FirstName)%20ge%2010))%20or%20(length(LastName)%20le%207))"; - - assertThat(query.getRequestQuery()).isEqualTo(expectedEncodedQuery); - } - - @Test - void testSafeCharsInFilter() - { - final ODataRequestRead query = - new DefaultTrippinService().getAllPeople().filter(Person.USER_NAME.contains("_*-:,/'().")).toRequest(); - - final String expectedEncodedQuery = "$filter=contains(UserName,'_*-:,/''().')"; - - assertThat(query.getRequestQuery()).isEqualTo(expectedEncodedQuery); - } - - @Test - void testSpecialCharsInFilter() - { - final ODataRequestRead query = - new DefaultTrippinService().getAllPeople().filter(Person.USER_NAME.contains("!@#$%^&=+|\\\"")).toRequest(); - - final String expectedEncodedQuery = "$filter=contains(UserName,'%21%40%23%24%25%5E%26%3D%2B%7C%5C%22')"; - - assertThat(query.getRequestQuery()).isEqualTo(expectedEncodedQuery); - } - - @Test - void testEncodingForeignCharactersInFilter() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .filter( - Person.USER_NAME - .equalTo("François") - .and(Person.LAST_NAME.contains("Sørina").or(Person.FIRST_NAME.matches("维基百科")))); - - final String expectedEncodedQuery = - "$filter=((UserName%20eq%20'Fran%C3%A7ois')%20and%20(contains(LastName,'S%C3%B8rina')%20or%20matchesPattern(FirstName,'%E7%BB%B4%E5%9F%BA%E7%99%BE%E7%A7%91')))"; - - assertThat(query.toRequest().getRequestQuery()).isEqualTo(expectedEncodedQuery); - } - - @Test - void testEncodingInNestedFiltersWithAllSpecialCharacters() - { - final ODataRequestRead request = - new DefaultTrippinService() - .getAllPeople() - .filter(Person.FIRST_NAME.contains("% $&#?\"\\+'")) - .select(Person.TO_BEST_FRIEND.select(Person.TO_TRIPS.filter(Trip.NAME.contains("% $&#?\"\\+'")))) - .toRequest(); - final String expected = - "$expand=BestFriend($expand=Trips($filter=contains(Name,'%25%20%24%26%23%3F%22%5C%2B''')))&$filter=contains(FirstName,'%25%20%24%26%23%3F%22%5C%2B''')"; - - assertThat(request.getRequestQuery()).isEqualTo(expected); - } - - @Test - void testEncodingInSearchQuery() - { - final ODataRequestRead search = - new DefaultTrippinService().getAllPeople().search("Hash # Quoted \"string\" Escaped \\").toRequest(); - final String query = search.getRequestQuery(); - - final String expected = "$search=%22Hash%20%23%20Quoted%20%5C%22string%5C%22%20Escaped%20%5C%5C%22"; - - assertThat(query).isEqualTo(expected); - } - - @Test - void testEncodingInOrderBy() - { - final ODataRequestRead request = - new DefaultTrippinService() - .getAllPeople() - .orderBy(Person.FIRST_NAME.asc(), Person.LAST_NAME.desc()) - .toRequest(); - - final String expected = "$orderby=FirstName%20asc,LastName%20desc"; - - assertThat(request.getRequestQuery()).isEqualTo(expected); - } - - @Test - void testEncodingInCustomQueryParametersWithAllSpecialCharacters() - { - final ODataRequestRead request = - new DefaultTrippinService() - .getAllPeople() - .withQueryParameter("foo", "hash#tag") - .withQueryParameter("param", "% $&#?\"\\+'bar") - .toRequest(); - final String expected = "foo=hash%23tag¶m=%25%20%24%26%23%3F%22%5C%2B'bar"; - - assertThat(request.getRequestQuery()).isEqualTo(expected); - } - - @Test - void testSafeCharsInCustomQueryParameters() - { - final ODataRequestRead request = - new DefaultTrippinService().getAllPeople().withQueryParameter("foo", "_*-:,/'().").toRequest(); - final String expected = "foo=_*-:,/'()."; - - assertThat(request.getRequestQuery()).isEqualTo(expected); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataClientBatchReferenceServiceIntegrationTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataClientBatchReferenceServiceIntegrationTest.java deleted file mode 100644 index 4b590ba9e0..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataClientBatchReferenceServiceIntegrationTest.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.mockito.Mockito.mock; - -import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.ParseException; -import org.apache.hc.core5.http.io.entity.EntityUtils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import com.google.gson.Gson; -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataRequestException; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCreate; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultMultipartGeneric; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; - -@Disabled( "Test runs against a v4 reference service on odata.org. Use it only to manually verify behaviour." ) -class ODataClientBatchReferenceServiceIntegrationTest -{ - private Destination httpDestination; - - @BeforeEach - void configure() - throws IOException, - ParseException - { - httpDestination = TripPinUtility.getDestination(); - } - - @Test - void testEmptyBatch() - throws IOException, - ParseException - { - final HttpClient httpClient = ApacheHttpClient5Accessor.getHttpClient(httpDestination); - - final ODataRequestResultMultipartGeneric batchResponse = - new ODataRequestBatch("/", ODataProtocol.V4).execute(httpClient); - - // response object not null - assertThat(batchResponse).isNotNull(); - - // response HTTP code is healthy - final ClassicHttpResponse httpResponse = batchResponse.getHttpResponse(); - assertThat(httpResponse.getCode()).isEqualTo(200); - - // response payload can be extracted - final String response = EntityUtils.toString(batchResponse.getHttpResponse().getEntity()); - assertThat(response).matches("^--batchresponse_[a-f0-9-]+--\r\n$"); - } - - @Test - void testBatchWithSingleRead() - throws IOException, - ParseException - { - final HttpClient httpClient = ApacheHttpClient5Accessor.getHttpClient(httpDestination); - - final ODataRequestResultMultipartGeneric batchResponse = - new ODataRequestBatch("/", ODataProtocol.V4) - .addRead(new ODataRequestRead("/", "People", "$top=1", ODataProtocol.V4)) - .execute(httpClient); - - // response object not null - assertThat(batchResponse).isNotNull(); - - // response HTTP code is healthy - final ClassicHttpResponse httpResponse = batchResponse.getHttpResponse(); - assertThat(httpResponse.getCode()).isEqualTo(200); - - // response payload can be extracted - final String response = EntityUtils.toString(batchResponse.getHttpResponse().getEntity()); - assertThat(response).isNotEmpty(); - - // response payload contains expected JSON result - final Matcher matcher = Pattern.compile("\"value\":\\[(.*?)]}\r\n").matcher(response); - assertThat(matcher.find()).isTrue(); - - // response JSON contains a valid Person - final Person person = new Gson().fromJson(matcher.group(1), Person.class); - assertThat(person).isNotNull().matches(p -> p.getUserName() != null); - } - - @Test - void testBatchWithReadWrite() - { - final HttpClient httpClient = ApacheHttpClient5Accessor.getHttpClient(httpDestination); - - final ODataRequestResultMultipartGeneric batchResponse = - new ODataRequestBatch("/", ODataProtocol.V4) - .addRead(new ODataRequestRead("/", "People", "$top=1", ODataProtocol.V4)) - .beginChangeset() - .addCreate(new ODataRequestCreate("/", "People", "{\"UserName\":\"JohnDoe1\"}", ODataProtocol.V4)) - .endChangeset() - .execute(httpClient); - - // response object not null - assertThat(batchResponse).isNotNull(); - - // response HTTP code is healthy - final ClassicHttpResponse httpResponse = batchResponse.getHttpResponse(); - assertThat(httpResponse.getCode()).isEqualTo(200); - } - - @Test - void testBatchErrorWithDifferentServicePath() - { - final HttpClient httpClient = mock(HttpClient.class); - - assertThatCode( - () -> new ODataRequestBatch("this/", ODataProtocol.V4) - .addRead(new ODataRequestRead("this/", "People", "$top=1", ODataProtocol.V4)) - .addRead(new ODataRequestRead("other/", "People", "$top=2", ODataProtocol.V4)) - .execute(httpClient)) - .isInstanceOf(ODataRequestException.class); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataClientBatchResponseParsingIntegrationTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataClientBatchResponseParsingIntegrationTest.java deleted file mode 100644 index 0caf4ca4da..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataClientBatchResponseParsingIntegrationTest.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; - -import java.io.IOException; -import java.util.List; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.io.entity.EntityUtils; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.cloudplatform.connectivity.ApacheHttpClient5Accessor; -import com.sap.cloud.sdk.cloudplatform.connectivity.Destination; -import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCreate; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultMultipartGeneric; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; - -@Disabled( "Test runs against a v4 reference service on odata.org. Use it only to manually verify behaviour." ) -class ODataClientBatchResponseParsingIntegrationTest -{ - private Destination httpDestination; - - @BeforeEach - void configure() - throws IOException - { - httpDestination = TripPinUtility.getDestination(); - } - - @Test - void testEmptyBatch() - { - final HttpClient httpClient = ApacheHttpClient5Accessor.getHttpClient(httpDestination); - - final ODataRequestResultMultipartGeneric batchResponse = - new ODataRequestBatch("/", ODataProtocol.V4).execute(httpClient); - - // response object not null - assertThat(batchResponse).isNotNull(); - - // extract unrequested read result - final ODataRequestRead read = new ODataRequestRead("/", "People", "$top=1", ODataProtocol.V4); - assertThatCode(() -> batchResponse.getResult(read)).isInstanceOf(IllegalArgumentException.class); - } - - @Test - void testBatchWithReads() - { - final HttpClient httpClient = ApacheHttpClient5Accessor.getHttpClient(httpDestination); - - final ODataRequestRead read1 = new ODataRequestRead("/", "People", "$top=1", ODataProtocol.V4); - final ODataRequestRead read2 = new ODataRequestRead("/", "People", "$top=2&$skip=1", ODataProtocol.V4); - final ODataRequestResultMultipartGeneric batchResponse = - new ODataRequestBatch("/", ODataProtocol.V4).addRead(read1).addRead(read2).execute(httpClient); - - // response object not null - assertThat(batchResponse).isNotNull(); - - // response HTTP code is healthy - assertThat(batchResponse.getHttpResponse().getCode()).isEqualTo(200); - - // response payload can be extracted - final List people1 = batchResponse.getResult(read1).asList(Person.class); - assertThat(people1).hasSize(1).doesNotContainNull(); - - final List people2 = batchResponse.getResult(read2).asList(Person.class); - assertThat(people2).hasSize(2).doesNotContainNull().doesNotContainAnyElementsOf(people1); - } - - @Test - void testBatchWithReadsAndWrites() - { - final HttpClient httpClient = ApacheHttpClient5Accessor.getHttpClient(httpDestination); - - final ODataRequestRead read1 = new ODataRequestRead("/", "People", "$top=1", ODataProtocol.V4); - final ODataRequestRead read2 = new ODataRequestRead("/", "People", "$top=2&$skip=1", ODataProtocol.V4); - - final ODataRequestCreate create1 = - new ODataRequestCreate( - "/", - "People", - "{\"UserName\":\"JohnDoe1\", \"FirstName\":\"John\"}", - ODataProtocol.V4); - final ODataRequestCreate create2 = - new ODataRequestCreate( - "/", - "People", - "{\"UserName\":\"JohnDoe2\", \"FirstName\":\"John\"}", - ODataProtocol.V4); - - final ODataRequestResultMultipartGeneric batchResponse = - new ODataRequestBatch("/", ODataProtocol.V4) - .addRead(read1) - .beginChangeset() - .addCreate(create1) - .addCreate(create2) - .endChangeset() - .addRead(read2) - .execute(httpClient); - - // response object not null - assertThat(batchResponse).isNotNull(); - - // response parsing - final List resultRead1 = batchResponse.getResult(read1).asList(Person.class); - final List resultRead2 = batchResponse.getResult(read2).asList(Person.class); - assertThat(resultRead1).isNotNull().hasSize(1).doesNotContainNull().doesNotContainAnyElementsOf(resultRead2); - assertThat(resultRead2).isNotNull().hasSize(2).doesNotContainNull().doesNotContainAnyElementsOf(resultRead1); - - assertThat(batchResponse.getResult(create1).as(Person.class)).isNotNull(); - assertThat(batchResponse.getResult(create2).as(Person.class)).isNotNull(); - - // response HTTP code is healthy - final ClassicHttpResponse httpResponse = batchResponse.getHttpResponse(); - assertThat(httpResponse.getCode()).isEqualTo(200); - } - - @Test - void testBatchResultWithError() - throws IOException - { - final HttpClient httpClient = ApacheHttpClient5Accessor.getHttpClient(httpDestination); - - final ODataRequestRead read = new ODataRequestRead("/", "People", "$top=1", ODataProtocol.V4); - - final ODataRequestResultMultipartGeneric batchResponse = - new ODataRequestBatch("/", ODataProtocol.V4).addRead(read).execute(httpClient); - - // consume result - EntityUtils.consume(batchResponse.getHttpResponse().getEntity()); - - // extract unrequested read result - assertThatCode(() -> batchResponse.getResult(read)).isInstanceOf(IllegalStateException.class); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataClientBatchResponseParsingUnitTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataClientBatchResponseParsingUnitTest.java deleted file mode 100644 index 28ce581daa..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataClientBatchResponseParsingUnitTest.java +++ /dev/null @@ -1,399 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol.V4; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -import java.io.IOException; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.hc.client5.http.classic.HttpClient; -import org.apache.hc.client5.http.classic.methods.HttpPost; -import org.apache.hc.core5.http.ClassicHttpRequest; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpHeaders; -import org.apache.hc.core5.http.io.entity.EntityUtils; -import org.apache.hc.core5.http.io.entity.StringEntity; -import org.apache.hc.core5.http.message.BasicClassicHttpResponse; -import org.junit.jupiter.api.Test; - -import com.google.common.base.Objects; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataResponseException; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataServiceErrorException; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestBatch; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestCreate; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestRead; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestReadByKey; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultMultipartGeneric; -import com.sap.cloud.sdk.datamodel.odatav4.TestUtility; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; - -import io.vavr.control.Try; -import lombok.Getter; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -class ODataClientBatchResponseParsingUnitTest -{ - private final AtomicInteger uuidCounter = new AtomicInteger(0); - private final Supplier uuidProvider = () -> new UUID(0, uuidCounter.incrementAndGet()); - - @Test - void testEmptyBatch() - { - // Read OData response json - final String requestBody = readResourceFileCrlf("BatchEmptyRequest.txt"); - final String responseBody = readResourceFileCrlf("BatchEmptyResponse.txt"); - - // Prepare test objects - final ODataRequestBatch batchRequest = new ODataRequestBatch("/", V4, uuidProvider); - - final HttpClient httpClient = MockedHttpClient.of(requestBody, responseBody); - final ODataRequestResultMultipartGeneric batchResponse = batchRequest.execute(httpClient); - - // Test assertion: response object not null and healthy - assertThat(batchResponse).isNotNull(); - assertThat(batchResponse.getHttpResponse().getCode()).isEqualTo(200); - - // extract unrequested read result - final ODataRequestRead read = new ODataRequestRead("/", "People", "$top=1", V4); - assertThatCode(() -> batchResponse.getResult(read)).isInstanceOf(IllegalArgumentException.class); - } - - @Test - void testBatchWithOnlyReads() - { - // Read OData response json - final String requestBody = readResourceFileCrlf("BatchOnlyReadsRequest.txt"); - final String responseBody = readResourceFileCrlf("BatchOnlyReadsResponse.txt"); - - // Prepare test objects - final ODataRequestRead read1 = new ODataRequestRead("/", "People", "$top=1", V4); - final ODataRequestRead read2 = new ODataRequestRead("/", "People", "$top=2&$skip=1", V4); - final ODataRequestBatch batchRequest = - new ODataRequestBatch("/", V4, uuidProvider).addRead(read1).addRead(read2); - - final HttpClient httpClient = MockedHttpClient.of(requestBody, responseBody); - final ODataRequestResultMultipartGeneric batchResponse = batchRequest.execute(httpClient); - - // Test assertion: response object not null and healthy - assertThat(batchResponse).isNotNull(); - assertThat(batchResponse.getHttpResponse().getCode()).isEqualTo(200); - - // Test assertion: response payload can be extracted - final List resultRead1 = batchResponse.getResult(read1).asList(Person.class); - final List resultRead2 = batchResponse.getResult(read2).asList(Person.class); - assertThat(resultRead1).isNotNull().hasSize(1).doesNotContainNull().doesNotContainAnyElementsOf(resultRead2); - assertThat(resultRead2).isNotNull().hasSize(2).doesNotContainNull().doesNotContainAnyElementsOf(resultRead1); - } - - @Test - void testBatchWithReadsOnMissingResponse() - { - // 2 requests but only 1 response - final String requestBody = readResourceFileCrlf("BatchOnlyReadsMissingRequest.txt"); - final String responseBody = readResourceFileCrlf("BatchOnlyReadsMissingResponse.txt"); - - // Prepare test objects - final ODataEntityKey entityKey1 = new ODataEntityKey(V4).addKeyProperty("key", "one"); - final ODataRequestReadByKey readByKey1 = new ODataRequestReadByKey("/", "People", entityKey1, "", V4); - final ODataEntityKey entityKey2 = new ODataEntityKey(V4).addKeyProperty("key", "two"); - final ODataRequestReadByKey readByKey2 = new ODataRequestReadByKey("/", "People", entityKey2, "", V4); - final ODataEntityKey entityKey3 = new ODataEntityKey(V4).addKeyProperty("key", "three"); - final ODataRequestReadByKey readByKey3 = new ODataRequestReadByKey("/", "People", entityKey3, "", V4); - final ODataRequestBatch batchRequest = - new ODataRequestBatch("/", V4, uuidProvider) - .addReadByKey(readByKey1) - .addReadByKey(readByKey2) - .addReadByKey(readByKey3); - - final HttpClient httpClient = MockedHttpClient.of(requestBody, responseBody); - final ODataRequestResultMultipartGeneric batchResponse = batchRequest.execute(httpClient); - - // Test assertion: response object not null and healthy - assertThat(batchResponse).isNotNull(); - assertThat(batchResponse.getHttpResponse().getCode()).isEqualTo(200); - - // Test assertion: - // response payload1 is 200 - assertThat(batchResponse.getResult(readByKey1)).isNotNull(); - - // response payload2 is 404 - assertThatExceptionOfType(ODataServiceErrorException.class) - .isThrownBy(() -> batchResponse.getResult(readByKey2)) - .satisfies(e -> assertThat(e.getHttpCode()).isEqualTo(404)); - - // response payload3 cannot be extracted, response is missing - assertThatExceptionOfType(ODataResponseException.class) - .isThrownBy(() -> batchResponse.getResult(readByKey3)) - .withMessage("Unable to extract batch response item at position 3. The response contains only 2 items."); - } - - @Test - void testBatchWithErrorReads() - { - // Read OData response json - final String requestBody = readResourceFileCrlf("BatchOnlyReadsRequest.txt"); - final String responseBody = readResourceFileCrlf("BatchOnlyReadsErrorResponse.txt"); - - // Prepare test objects - final ODataRequestRead read1 = new ODataRequestRead("/", "People", "$top=1", V4); - final ODataRequestRead read2 = new ODataRequestRead("/", "People", "$top=2&$skip=1", V4); - final ODataRequestBatch batchRequest = - new ODataRequestBatch("/", V4, uuidProvider).addRead(read1).addRead(read2); - - final HttpClient httpClient = MockedHttpClient.of(requestBody, responseBody); - final ODataRequestResultMultipartGeneric batchResponse = batchRequest.execute(httpClient); - - // Test assertion: response object not null and healthy - assertThat(batchResponse).isNotNull(); - assertThat(batchResponse.getHttpResponse().getCode()).isEqualTo(200); - - // Test assertion: response payload can be extracted - assertThat(batchResponse.getResult(read1)).isNotNull(); - - // batchResponse.getResult(read2); // expected error: - assertThatExceptionOfType(ODataServiceErrorException.class) - .isThrownBy(() -> batchResponse.getResult(read2)) - .satisfies(e -> { - assertThat(e.getHttpCode()).isEqualTo(400); - assertThat(e.getOdataError().getODataCode()).isEqualTo("ZCU/100"); - }); - } - - @Test - void testBatchWithReadsAndWrites() - { - // Read OData response json - final String requestBody = readResourceFileCrlf("BatchReadsAndWritesSuccessRequest.txt"); - final String responseBody = readResourceFileCrlf("BatchReadsAndWritesSuccessResponse.txt"); - - // Prepare test objects - final ODataRequestCreate create1 = - new ODataRequestCreate("/", "People", "{\"UserName\":\"JohnDoe1\", \"FirstName\":\"John\"}", V4); - final ODataRequestCreate create2 = - new ODataRequestCreate("/", "People", "{\"UserName\":\"JohnDoe2\", \"FirstName\":\"John\"}", V4); - final ODataEntityKey entityKey = new ODataEntityKey(V4).addKeyProperty("key", "foo"); - final ODataRequestReadByKey readByKey = new ODataRequestReadByKey("/", "People", entityKey, "", V4); - final ODataRequestBatch batchRequest = - new ODataRequestBatch("/", V4, uuidProvider) - .beginChangeset() - .addCreate(create1) - .addCreate(create2) - .endChangeset() - .addReadByKey(readByKey); - - final HttpClient httpClient = MockedHttpClient.of(requestBody, responseBody); - final ODataRequestResultMultipartGeneric batchResponse = batchRequest.execute(httpClient); - - // Test assertion: response object not null and healthy - assertThat(batchResponse).isNotNull(); - assertThat(batchResponse.getHttpResponse().getCode()).isEqualTo(200); - - // Test assertion: response parsing - final ODataRequestResultGeneric resultCreate1 = batchResponse.getResult(create1); - assertThat(resultCreate1).isNotNull(); - assertThat(resultCreate1.as(Person.class)).isNotNull().extracting(Person::getUserName).isEqualTo("JohnDoe1"); - assertThat(resultCreate1.getHttpResponse().getCode()).isEqualTo(201); - - final ODataRequestResultGeneric resultCreate2 = batchResponse.getResult(create2); - assertThat(resultCreate2).isNotNull(); - assertThat(resultCreate2.as(Person.class)).isNotNull().extracting(Person::getUserName).isEqualTo("JohnDoe2"); - assertThat(resultCreate2.getHttpResponse().getCode()).isEqualTo(201); - - assertThatExceptionOfType(ODataServiceErrorException.class) - .isThrownBy(() -> batchResponse.getResult(readByKey)) - .satisfies(e -> assertThat(e.getHttpCode()).isEqualTo(404)); - } - - @Test - void testBatchWithErrorInChangeset() - throws IOException - { - // Read OData response json - final String requestBody = readResourceFileCrlf("BatchReadsAndWritesErrorRequest.txt"); - final String responseBody = readResourceFileCrlf("BatchReadsAndWritesErrorResponse.txt"); - - // Prepare test objects - final ODataRequestCreate create1 = - new ODataRequestCreate("/", "People", "{\"UserName\":\"JohnDoe1\", \"FirstName\":\"John\"}", V4); - final ODataRequestCreate create2 = new ODataRequestCreate("/", "People", "{\"UserName\":\"JohnDoe2\"}", V4); - final ODataEntityKey entityKey = new ODataEntityKey(V4).addKeyProperty("key", "klauskinski"); - final ODataRequestReadByKey readByKey = new ODataRequestReadByKey("/", "People", entityKey, "", V4); - final ODataRequestBatch requestBatch = - new ODataRequestBatch("/", V4, uuidProvider) - .beginChangeset() - .addCreate(create1) - .addCreate(create2) - .endChangeset() - .addReadByKey(readByKey); - - final HttpClient httpClient = MockedHttpClient.of(requestBody, responseBody); - final ODataRequestResultMultipartGeneric batchResponse = requestBatch.execute(httpClient); - - // Test assertion: response object not null and healthy - assertThat(batchResponse).isNotNull(); - assertThat(batchResponse.getHttpResponse().getCode()).isEqualTo(200); - - // Test assertion: response parsing - assertThatExceptionOfType(ODataServiceErrorException.class) - .isThrownBy(() -> batchResponse.getResult(create1)) - .satisfies(e -> { - assertThat(e.getHttpCode()).isEqualTo(400); - assertThat(e.getHttpBody().get()).contains("The FirstName field is required"); - assertThat(e.getRequest()).isSameAs(create2); - }); - - assertThatExceptionOfType(ODataServiceErrorException.class) - .isThrownBy(() -> batchResponse.getResult(create2)) - .satisfies(e -> { - assertThat(e.getHttpCode()).isEqualTo(400); - assertThat(e.getHttpBody().get()).contains("The FirstName field is required"); - assertThat(e.getRequest()).isSameAs(create2); - }); - - final ODataRequestResultGeneric resultReadByKey = batchResponse.getResult(readByKey); - assertThat(resultReadByKey).isNotNull(); - assertThat(resultReadByKey.getHttpResponse().getCode()).isEqualTo(200); - } - - @Test - void testBatchWithErrorInsteadOfChangeset() - throws IOException - { - // Read OData response json - final String requestBody = readResourceFileCrlf("BatchReadsAndWritesErrorRequest.txt"); - final String responseBody = readResourceFileCrlf("BatchReadsAndWritesErrorResponseWithoutChangeset.txt"); - - // Prepare test objects - final ODataRequestCreate create1 = - new ODataRequestCreate("/", "People", "{\"UserName\":\"JohnDoe1\", \"FirstName\":\"John\"}", V4); - final ODataRequestCreate create2 = new ODataRequestCreate("/", "People", "{\"UserName\":\"JohnDoe2\"}", V4); - final ODataEntityKey entityKey = new ODataEntityKey(V4).addKeyProperty("key", "klauskinski"); - final ODataRequestReadByKey readByKey = new ODataRequestReadByKey("/", "People", entityKey, "", V4); - final ODataRequestBatch batchRequest = - new ODataRequestBatch("/", V4, uuidProvider) - .beginChangeset() - .addCreate(create1) - .addCreate(create2) - .endChangeset() - .addReadByKey(readByKey); - - final HttpClient httpClient = MockedHttpClient.of(requestBody, responseBody); - final ODataRequestResultMultipartGeneric batchResponse = batchRequest.execute(httpClient); - - // Test assertion: response object not null and healthy - assertThat(batchResponse).isNotNull(); - assertThat(batchResponse.getHttpResponse().getCode()).isEqualTo(200); - - // Test assertion: response parsing - assertThatExceptionOfType(ODataResponseException.class) - .isThrownBy(() -> batchResponse.getResult(create1)) - .satisfies(e -> { - assertThat(e.getHttpCode()).isEqualTo(400); - assertThat(e.getHttpBody().get()).contains("The FirstName field is required"); - assertThat(e.getRequest()).isSameAs(create2); - }); - - assertThatExceptionOfType(ODataResponseException.class) - .isThrownBy(() -> batchResponse.getResult(create2)) - .satisfies(e -> { - assertThat(e.getHttpCode()).isEqualTo(400); - assertThat(e.getHttpBody().get()).contains("The FirstName field is required"); - assertThat(e.getRequest()).isSameAs(create2); - }); - - final ODataRequestResultGeneric resultReadByKey = batchResponse.getResult(readByKey); - assertThat(resultReadByKey).isNotNull(); - assertThat(resultReadByKey.getHttpResponse().getCode()).isEqualTo(200); - } - - @Test - void testBatchResultWithError() - { - // Prepare test objects - final ODataRequestRead read = new ODataRequestRead("/", "People", "$top=1", V4); - final ODataRequestBatch batchRequest = new ODataRequestBatch("/", V4, uuidProvider).addRead(read); - - final HttpClient httpClient = MockedHttpClient.of(null, null); - final ODataRequestResultMultipartGeneric batchResponse = batchRequest.execute(httpClient); - - assertThat(batchResponse.getHttpResponse().getEntity()).isNull(); - - // Extract requested read result - entity already consumed - assertThatCode(() -> batchResponse.getResult(read)).isInstanceOf(ODataResponseException.class); - } - - private static class MockedHttpClient - { - @Getter - private final HttpClient httpClient = mock(HttpClient.class); - - @SneakyThrows - static HttpClient of( @Nullable final String requestBody, @Nullable final String responseBody ) - { - final ClassicHttpResponse odataResponse = new BasicClassicHttpResponse(200, "OK"); - if( responseBody != null ) { - odataResponse.setEntity(new StringEntity(responseBody)); - final String batchDelimiter = responseBody.substring(2, responseBody.indexOf("\r")); - odataResponse.setHeader(HttpHeaders.CONTENT_TYPE, "multipart/mixed; boundary=" + batchDelimiter); - } - - final HttpClient httpClient = mock(HttpClient.class); - doReturn(odataResponse) - .when(httpClient) - .executeOpen(isNull(), argThat(req -> isCorrectODataRequest(req, requestBody)), isNull()); - - return httpClient; - } - - static private - boolean - isCorrectODataRequest( @Nonnull final ClassicHttpRequest request, @Nullable final String assertRequestBody ) - { - if( !(request instanceof HttpPost) ) { - log.error("Expected HTTP POST request."); - return false; - } - - final Header requestedContentType = request.getFirstHeader(HttpHeaders.CONTENT_TYPE); - final String expectedContentType = "multipart/mixed;boundary=batch_00000000-0000-0000-0000-000000000001"; - if( !requestedContentType.getValue().equals(expectedContentType) ) { - log.error("Expected content type: {}", expectedContentType); - return false; - } - - if( assertRequestBody == null ) { - return true; - } - - final String requestCont = Try.of(() -> EntityUtils.toString(((HttpPost) request).getEntity())).getOrNull(); - if( !Objects.equal(requestCont, assertRequestBody) ) { - log.error("Expected request content: {}, but got {}", assertRequestBody, requestCont); - return false; - } - - return true; - } - } - - private static String readResourceFileCrlf( final String file ) - { - return TestUtility.readResourceFileCrlf(ODataClientBatchResponseParsingUnitTest.class, file); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataReferenceServiceByKeyTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataReferenceServiceByKeyTest.java deleted file mode 100644 index 3233416dd1..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataReferenceServiceByKeyTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestReadByKey; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Trip; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class ODataReferenceServiceByKeyTest -{ - private static final String testUserName = "testuser"; - private static final String keyWithSpecialCharacters = "test''user"; - private static final String fullQuery = - DefaultTrippinService.DEFAULT_SERVICE_PATH + "/People('" + testUserName + "')?%s"; - private static final String fullQueryWithSpecialCharacters = - DefaultTrippinService.DEFAULT_SERVICE_PATH + "/People('" + keyWithSpecialCharacters + "')"; - - @Test - void testSelectAndExpand() - { - final ODataRequestReadByKey request = - new DefaultTrippinService() - .getPeopleByKey(testUserName) - .select(Person.FIRST_NAME, Person.LAST_NAME) - .select(Person.TO_TRIPS) - .toRequest(); - - final String expected = "$select=FirstName,LastName&$expand=Trips"; - - assertThat(request.getQueryString()).isEqualTo(expected); - assertThat(request.getRelativeUri()).hasToString(String.format(fullQuery, expected)); - } - - @Test - void testMultipleExpandsWithFilter() - { - final ODataRequestReadByKey request = - new DefaultTrippinService() - .getPeopleByKey(testUserName) - .select(Person.TO_TRIPS.select(Trip.NAME)) - .select(Person.TO_BEST_FRIEND.select(Person.LAST_NAME)) - .toRequest(); - - final String expected = "$expand=Trips($select=Name),BestFriend($select=LastName)"; - - assertThat(request.getQueryString()).isEqualTo(expected); - assertThat(request.getRelativeUri()).hasToString(String.format(fullQuery, expected)); - } - - @Test - void testAllSelectionVariants() - { - final ODataRequestReadByKey request = - new DefaultTrippinService() - .getPeopleByKey(testUserName) - .select(Person.GENDER, Person.USER_NAME) - .select(Person.EMAILS) - .select(Person.TO_BEST_FRIEND, Person.TO_TRIPS, Person.TO_FRIENDS.top(2).skip(1)) - .toRequest(); - - final String expected = "$select=Gender,UserName,Emails&$expand=BestFriend,Trips,Friends($top=2;$skip=1)"; - - assertThat(request.getQueryString()).isEqualTo(expected); - assertThat(request.getRelativeUri()).hasToString(String.format(fullQuery, expected)); - } - - @Test - void testGetByKeyWithSpecialCharacters() - { - final ODataRequestReadByKey request = new DefaultTrippinService().getPeopleByKey("test'user").toRequest(); - assertThat(request.getRelativeUri()).hasToString(fullQueryWithSpecialCharacters); - } - - @Test - void testAllDuplicateSelections() - { - final ODataRequestReadByKey request = - new DefaultTrippinService() - .getPeopleByKey(testUserName) - .select(Person.GENDER, Person.USER_NAME) - .select(Person.GENDER, Person.USER_NAME) - .select(Person.TO_BEST_FRIEND, Person.TO_TRIPS) - .select(Person.TO_BEST_FRIEND, Person.TO_BEST_FRIEND.select(Person.TO_FRIENDS)) - .select(Person.TO_FRIENDS, Person.TO_FRIENDS.top(2).skip(1)) - .toRequest(); - - final String expected = - "$select=Gender,UserName&$expand=Trips,BestFriend($expand=Friends),Friends($top=2;$skip=1)"; - - assertThat(request.getQueryString()).isEqualTo(expected); - assertThat(request.getRelativeUri()).hasToString(String.format(fullQuery, expected)); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataReferenceServiceTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataReferenceServiceTest.java deleted file mode 100644 index 480aef01cb..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataReferenceServiceTest.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Trip; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class ODataReferenceServiceTest -{ - @Test - void testSelectAndExpand() - { - final GetAllRequestBuilder request = - new DefaultTrippinService() - .getAllPeople() - .select(Person.FIRST_NAME, Person.LAST_NAME) - .select(Person.TO_TRIPS); - - assertThat(request.toRequest().getRelativeUri()) - .hasParameter("$select", "FirstName,LastName") - .hasParameter("$expand", "Trips"); - } - - @Test - void testMultipleExpandsWithFilter() - { - final GetAllRequestBuilder request = - new DefaultTrippinService() - .getAllPeople() - .select(Person.TO_TRIPS.select(Trip.NAME)) - .select(Person.TO_BEST_FRIEND.select(Person.LAST_NAME)); - - assertThat(request.toRequest().getRelativeUri()) - .hasParameter("$expand", "Trips($select=Name),BestFriend($select=LastName)"); - } - - @Test - void testAllSelectionVariants() - { - final GetAllRequestBuilder request = - new DefaultTrippinService() - .getAllPeople() - .select(Person.GENDER, Person.USER_NAME) - .select(Person.TO_BEST_FRIEND, Person.TO_TRIPS, Person.TO_FRIENDS.top(2).skip(1)) - .filter(Person.EMAILS.contains(Collections.singletonList("ASD"))); - - assertThat(request.toRequest().getRelativeUri()) - .hasParameter("$select", "Gender,UserName") - .hasParameter("$expand", "BestFriend,Trips,Friends($top=2;$skip=1)") - .hasParameter("$filter", "contains(Emails,['ASD'])"); - } - - @Test - void testFiltersWithSpecialCharacters() - { - final GetAllRequestBuilder request = - new DefaultTrippinService().getAllPeople().filter(Person.FIRST_NAME.contains("' +&#\\")); - - assertThat(request.toRequest().getRelativeUri()).hasParameter("$filter", "contains(FirstName,''' +&#\\')"); - } - - @Test - void testNestedFiltersWithAllSpecialCharacters() - { - final GetAllRequestBuilder request = - new DefaultTrippinService() - .getAllPeople() - .filter(Person.FIRST_NAME.contains("% $&#?\"\\+'")) - .select(Person.TO_BEST_FRIEND.select(Person.TO_TRIPS.filter(Trip.NAME.contains("% $&#?\"\\+'")))); - - assertThat(request.toRequest().getRelativeUri()) - .hasParameter("$expand", "BestFriend($expand=Trips($filter=contains(Name,'% $&#?\"\\+''')))") - .hasParameter("$filter", "contains(FirstName,'% $&#?\"\\+''')"); - } - - @Test - void testNestedFiltersWithSpecialCharacters() - { - final GetAllRequestBuilder request = - new DefaultTrippinService() - .getAllPeople() - .filter(Person.FIRST_NAME.contains("' +&#\\")) - .select(Person.TO_TRIPS.filter(Trip.NAME.equalTo("Trip in '&USA'#"))); - - assertThat(request.toRequest().getRelativeUri()) - .hasParameter("$expand", "Trips($filter=(Name eq 'Trip in ''&USA''#'))") - .hasParameter("$filter", "contains(FirstName,''' +&#\\')"); - } - - @Test - void testAllDuplicateSelections() - { - final GetAllRequestBuilder request = - new DefaultTrippinService() - .getAllPeople() - .select(Person.GENDER, Person.USER_NAME) - .select(Person.GENDER, Person.USER_NAME) - .select(Person.TO_BEST_FRIEND, Person.TO_TRIPS) - .select(Person.TO_BEST_FRIEND, Person.TO_BEST_FRIEND.select(Person.TO_FRIENDS)) - .select(Person.TO_FRIENDS, Person.TO_FRIENDS.top(2).skip(1)); - - assertThat(request.toRequest().getRelativeUri()) - .hasParameter("$select", "Gender,UserName") - .hasParameter("$expand", "Trips,BestFriend($expand=Friends),Friends($top=2;$skip=1)"); - } - - @Test - void testOrderBy() - { - final GetAllRequestBuilder request = - new DefaultTrippinService().getAllPeople().orderBy(Person.FIRST_NAME.asc(), Person.LAST_NAME.desc()); - - assertThat(request.toRequest().getRelativeUri()).hasParameter("$orderby", "FirstName asc,LastName desc"); - } - - @Test - void testBadOrderByUsage() - { - final GetAllRequestBuilder request = - new DefaultTrippinService() - .getAllPeople() - .orderBy(Person.FIRST_NAME.desc(), Person.LAST_NAME.asc(), Person.FIRST_NAME.desc()); - - assertThat(request.toRequest().getRelativeUri()).hasParameter("$orderby", "FirstName desc,LastName asc"); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataV4BatchReferenceServiceUnitTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataV4BatchReferenceServiceUnitTest.java deleted file mode 100644 index 1e501349b2..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/ODataV4BatchReferenceServiceUnitTest.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; -import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; -import static com.github.tomakehurst.wiremock.client.WireMock.head; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; -import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; -import static com.sap.cloud.sdk.datamodel.odatav4.TestUtility.readResourceFileCrlf; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; - -import javax.annotation.Nonnull; - -import org.apache.hc.core5.http.HttpHeaders; -import org.apache.hc.core5.http.HttpStatus; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import com.github.tomakehurst.wiremock.core.WireMockConfiguration; -import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; -import com.github.tomakehurst.wiremock.junit5.WireMockTest; -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataServiceErrorException; -import com.sap.cloud.sdk.datamodel.odatav4.core.BatchRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.BatchResponse; -import com.sap.cloud.sdk.datamodel.odatav4.core.CreateRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.GetByKeyRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.TrippinService; - -@WireMockTest -class ODataV4BatchReferenceServiceUnitTest -{ - private static final WireMockConfiguration WIREMOCK_CONFIGURATION = wireMockConfig().dynamicPort(); - - private static final String X_CSRF_TOKEN_HEADER_KEY = "x-csrf-token"; - private static final String X_CSRF_TOKEN_HEADER_FETCH_VALUE = "fetch"; - private static final String X_CSRF_TOKEN_HEADER_VALUE = "awesome-csrf-token"; - - private DefaultHttpDestination destination; - private TrippinService service; - private BatchRequestBuilder sut; - - @BeforeEach - void setup( @Nonnull final WireMockRuntimeInfo wm ) - { - destination = DefaultHttpDestination.builder(wm.getHttpBaseUrl()).build(); - service = new DefaultTrippinService(); - - final AtomicInteger uuidCounter = new AtomicInteger(); - - sut = new BatchRequestBuilder(TrippinService.DEFAULT_SERVICE_PATH) - { - @Override - protected Supplier getUuidProvider() - { - return () -> new UUID(0, uuidCounter.incrementAndGet()); - } - }; - - // mock CSRF token retrieval - stubFor(head(anyUrl()).willReturn(ok())); - stubFor( - head(urlEqualTo("/")) - .withHeader(X_CSRF_TOKEN_HEADER_KEY, equalTo(X_CSRF_TOKEN_HEADER_FETCH_VALUE)) - .willReturn(ok().withHeader(X_CSRF_TOKEN_HEADER_KEY, X_CSRF_TOKEN_HEADER_VALUE))); - } - - @Test - void testEmptyBatch() - { - // Read OData response json - final String requestBody = - readResourceFileCrlf(ODataV4BatchReferenceServiceUnitTest.class, "BatchEmptyRequest.txt"); - final String responseBody = - readResourceFileCrlf(ODataV4BatchReferenceServiceUnitTest.class, "BatchEmptyResponse.txt"); - final String batchRequestUrl = String.format("%s%s", TrippinService.DEFAULT_SERVICE_PATH, "/$batch"); - - // Mocking OData response - final String responseContentType = - "multipart/mixed; boundary=batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef"; - stubFor( - post(urlEqualTo(batchRequestUrl)) - .willReturn(okForContentType(responseContentType, responseBody).withStatus(HttpStatus.SC_ACCEPTED))); - - // Run payload - final BatchResponse batchResponse = sut.execute(destination); - - // Verify request body - final String requestContentType = "multipart/mixed;boundary=batch_00000000-0000-0000-0000-000000000001"; - verify( - postRequestedFor(urlEqualTo(batchRequestUrl)) - .withoutHeader(HttpHeaders.ACCEPT) - .withHeader(HttpHeaders.CONTENT_TYPE, equalTo(requestContentType)) - .withRequestBody(equalTo(requestBody))); - - // Test assertion: response object not null and healthy - assertThat(batchResponse.getResponseStatusCode()).isEqualTo(HttpStatus.SC_ACCEPTED); - assertThat(batchResponse.getResponseHeaders()).isNotEmpty(); - } - - @Test - void testBatchWithOnlyReads() - { - // Read OData response json - final String requestBody = - readResourceFileCrlf(ODataV4BatchReferenceServiceUnitTest.class, "BatchOnlyReadsRequest.txt"); - final String responseBody = - readResourceFileCrlf(ODataV4BatchReferenceServiceUnitTest.class, "BatchOnlyReadsResponse.txt"); - final String batchRequestUrl = String.format("%s%s", TrippinService.DEFAULT_SERVICE_PATH, "/$batch"); - - // Mocking OData response - final String responseContentType = - "multipart/mixed; boundary=batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef"; - stubFor( - post(urlEqualTo(batchRequestUrl)) - .willReturn(okForContentType(responseContentType, responseBody).withStatus(HttpStatus.SC_ACCEPTED))); - - // Prepare test objects - final GetAllRequestBuilder getAll1 = service.getAllPeople().top(1); - final GetAllRequestBuilder getAll2 = service.getAllPeople().top(2).skip(1); - - // build batch request - final BatchResponse batchResponse = sut.addReadOperations(getAll1, getAll2).execute(destination); - - // Verify request body - final String requestContentType = "multipart/mixed;boundary=batch_00000000-0000-0000-0000-000000000001"; - verify( - postRequestedFor(urlEqualTo(batchRequestUrl)) - .withoutHeader(HttpHeaders.ACCEPT) - .withHeader(HttpHeaders.CONTENT_TYPE, equalTo(requestContentType)) - .withRequestBody(equalTo(requestBody))); - - // Test assertion: response object not null and healthy - assertThat(batchResponse.getResponseStatusCode()).isEqualTo(HttpStatus.SC_ACCEPTED); - assertThat(batchResponse.getResponseHeaders()).isNotEmpty(); - } - - @Test - void testBatchWithErrorReads() - { - // Read OData response json - final String requestBody = - readResourceFileCrlf(ODataV4BatchReferenceServiceUnitTest.class, "BatchOnlyReadsRequest.txt"); - final String responseBody = - readResourceFileCrlf(ODataV4BatchReferenceServiceUnitTest.class, "BatchOnlyReadsErrorResponse.txt"); - final String batchRequestUrl = String.format("%s%s", TrippinService.DEFAULT_SERVICE_PATH, "/$batch"); - - // Mocking OData response - final String responseContentType = - "multipart/mixed; boundary=batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef"; - stubFor( - post(urlEqualTo(batchRequestUrl)) - .willReturn(okForContentType(responseContentType, responseBody).withStatus(HttpStatus.SC_ACCEPTED))); - - // Prepare test objects - final GetAllRequestBuilder getAll1 = service.getAllPeople().top(1); - final GetAllRequestBuilder getAll2 = service.getAllPeople().top(2).skip(1); - - // build batch request - final BatchResponse batchResponse = sut.addReadOperations(getAll1, getAll2).execute(destination); - - assertThat(batchResponse.getReadResult(getAll1)).isNotEmpty(); - - assertThatExceptionOfType(ODataServiceErrorException.class) - .isThrownBy(() -> batchResponse.getReadResult(getAll2)) - .satisfies(e -> { - assertThat(e.getHttpCode()).isEqualTo(400); - assertThat(e.getOdataError().getODataCode()).isEqualTo("ZCU/100"); - }); - - // Verify request body - final String requestContentType = "multipart/mixed;boundary=batch_00000000-0000-0000-0000-000000000001"; - verify( - postRequestedFor(urlEqualTo(batchRequestUrl)) - .withoutHeader(HttpHeaders.ACCEPT) - .withHeader(HttpHeaders.CONTENT_TYPE, equalTo(requestContentType)) - .withRequestBody(equalTo(requestBody))); - - // Test assertion: response object not null and healthy - assertThat(batchResponse.getResponseStatusCode()).isEqualTo(HttpStatus.SC_ACCEPTED); - assertThat(batchResponse.getResponseHeaders()).isNotEmpty(); - } - - @Test - void testBatchWithReadsAndWrites() - { - // read response json - final String serverResponse = - readResourceFileCrlf(ODataV4BatchReferenceServiceUnitTest.class, "BatchReadsAndWritesSuccessResponse.txt"); - final String batchRequestBody = - readResourceFileCrlf(ODataV4BatchReferenceServiceUnitTest.class, "BatchReadsAndWritesSuccessRequest.txt"); - final String batchRequestUrl = String.format("%s%s", TrippinService.DEFAULT_SERVICE_PATH, "/$batch"); - - final String responseContentType = - "multipart/mixed; boundary=batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef"; - - // Mocking S/4 Hana - stubFor( - post(urlEqualTo(batchRequestUrl)) - .willReturn(okForContentType(responseContentType, serverResponse).withStatus(HttpStatus.SC_ACCEPTED))); - - Person person1 = Person.builder().userName("JohnDoe1").firstName("John").build(); - Person person2 = Person.builder().userName("JohnDoe2").firstName("John").build(); - - // prepare test objects - final CreateRequestBuilder createPersonRequest1 = service.createPeople(person1); - final CreateRequestBuilder createPersonRequest2 = service.createPeople(person2); - final GetByKeyRequestBuilder getPersonByKeyRequest = service.getPeopleByKey("foo"); - - // build batch request - final BatchResponse batchResponse = - sut - .addChangeset(createPersonRequest1, createPersonRequest2) - .addReadOperations(getPersonByKeyRequest) - .execute(destination); - - assertThat(batchResponse.getResponseStatusCode()).isEqualTo(HttpStatus.SC_ACCEPTED); - assertThat(batchResponse.getResponseHeaders()).isNotEmpty(); - - // verify request body - final String requestContentType = "multipart/mixed;boundary=batch_00000000-0000-0000-0000-000000000001"; - verify( - postRequestedFor(urlEqualTo(batchRequestUrl)) - .withoutHeader(HttpHeaders.ACCEPT) - .withHeader(HttpHeaders.CONTENT_TYPE, equalTo(requestContentType)) - .withRequestBody(equalTo(batchRequestBody))); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/PropertyCustomSerializationTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/PropertyCustomSerializationTest.java deleted file mode 100644 index bde2f899d9..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/PropertyCustomSerializationTest.java +++ /dev/null @@ -1,175 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.Month; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; - -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -class PropertyCustomSerializationTest -{ - private static final User USER = - new User( - LocalDate.of(1960, Month.MAY, 31).atTime(18, 0).atOffset(ZoneOffset.ofHours(1)), - LocalDate.of(2020, Month.FEBRUARY, 20).atStartOfDay().atZone(ZoneOffset.UTC), - LocalDate.of(2005, Month.DECEMBER, 24).atStartOfDay()); - - @EqualsAndHashCode( callSuper = true ) - @RequiredArgsConstructor - @AllArgsConstructor - @JsonAdapter( GsonVdmAdapterFactory.class ) - @JsonSerialize( using = JacksonVdmObjectSerializer.class ) - @JsonDeserialize( using = JacksonVdmObjectDeserializer.class ) - public static class User extends VdmEntity - { - @Getter - private final String entityCollection = "People"; - - @Getter - private final String odataType = "Example.User"; - - @Getter - private final Class type = User.class; - - @ElementName( "DateOfBirth" ) - private OffsetDateTime dateOfBirth; - - @ElementName( "LocaleTime" ) - @JsonSerialize( using = CustomZonedDateSerializer.class ) - @JsonDeserialize( using = CustomZonedDateDeserializer.class ) - @JsonAdapter( ZonedDateTimeAdapter.class ) - private ZonedDateTime localeTime; - - @ElementName( "RegistrationDate" ) - @JsonSerialize( using = CustomLocalDateSerializer.class ) - @JsonDeserialize( using = CustomLocalDateDeserializer.class ) - @JsonAdapter( LocalDateTimeAdapter.class ) - private LocalDateTime registrationDate; - } - - @Test - void testSerializationWithGson() - { - final String json = new Gson().toJson(USER); - final User actual = new Gson().fromJson(json, User.class); - assertThat(actual).isEqualTo(USER); - } - - @Test - void testSerializationWithJackson() - throws JsonProcessingException - { - final String json = new ObjectMapper().writeValueAsString(USER); - final User actual = new ObjectMapper().readValue(json, User.class); - assertThat(actual).isEqualTo(USER); - } - - // Custom serializers and deserializers - - public static class CustomLocalDateSerializer extends JsonSerializer - { - @Override - public void serialize( final LocalDateTime val, final JsonGenerator gen, final SerializerProvider serializers ) - throws IOException - { - gen.writeString(val.atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); - } - } - - public static class CustomZonedDateSerializer extends JsonSerializer - { - @Override - public void serialize( final ZonedDateTime val, final JsonGenerator gen, final SerializerProvider serializers ) - throws IOException - { - gen.writeString(val.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); - } - } - - public static class CustomLocalDateDeserializer extends JsonDeserializer - { - @Override - public LocalDateTime deserialize( final JsonParser p, final DeserializationContext ctxt ) - throws IOException - { - return OffsetDateTime.parse(p.getValueAsString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME).toLocalDateTime(); - } - } - - public static class CustomZonedDateDeserializer extends JsonDeserializer - { - @Override - public ZonedDateTime deserialize( final JsonParser p, final DeserializationContext ctxt ) - throws IOException - { - return OffsetDateTime.parse(p.getValueAsString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME).toZonedDateTime(); - } - } - - public static class LocalDateTimeAdapter extends TypeAdapter - { - @Override - public void write( final JsonWriter out, final LocalDateTime value ) - throws IOException - { - out.value(value.atZone(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); - } - - @Override - public LocalDateTime read( final JsonReader in ) - throws IOException - { - return OffsetDateTime.parse(in.nextString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME).toLocalDateTime(); - } - } - - public static class ZonedDateTimeAdapter extends TypeAdapter - { - @Override - public void write( final JsonWriter out, final ZonedDateTime value ) - throws IOException - { - out.value(value.toOffsetDateTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); - } - - @Override - public ZonedDateTime read( final JsonReader in ) - throws IOException - { - return OffsetDateTime.parse(in.nextString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME).toZonedDateTime(); - } - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/PropertySerializationTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/PropertySerializationTest.java deleted file mode 100644 index 0466994ee8..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/PropertySerializationTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.LocalDate; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.Arrays; - -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.City; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Location; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.PersonGender; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Trip; - -class PropertySerializationTest -{ - @Test - void testEnum() - { - final Person person = Person.builder().gender(PersonGender.FEMALE).firstName("Eve").build(); - final String json = new Gson().toJson(person); - final JsonElement actual = new Gson().toJsonTree(person); - assertThat(actual) - .isEqualTo( - JsonParser - .parseString( - "{\"FirstName\":\"Eve\",\"Gender\":\"Female\",\"@odata.type\":\"#Trippin.Person\",\"Friends\":[],\"Trips\":[]}")); - } - - @Test - void testComplexCollection() - { - final City city = new City(); - city.setName("Potsdam"); - city.setRegion("Nedlitz"); - city.setCountryRegion("Brandenburg"); - - final Location location = new Location(); - location.setAddress("Konrad-Zuse-Ring 10, 14469"); - location.setCity(city); - - final Person person = Person.builder().addressInfo(Arrays.asList(location)).build(); - - final JsonElement actual = new Gson().toJsonTree(person); - assertThat(actual).isEqualTo(JsonParser.parseString(""" - {"AddressInfo":[\ - {"Address":"Konrad-Zuse-Ring 10, 14469",\ - "City":{"Name":"Potsdam","CountryRegion":"Brandenburg","Region":"Nedlitz","@odata.type":"#Trippin.City"},\ - "@odata.type":"#Trippin.Location"}\ - ],"@odata.type":"#Trippin.Person"\ - ,"Friends":[],"Trips":[]}\ - """)); - // {"@odata.type":"#Trippin.Person","AddressInfo":[{"@odata.type":"#Trippin.Location","Address":"Konrad-Zuse-Ring 10, 14469","City":{"@odata.type":"#Trippin.City","Name":"Potsdam","CountryRegion":"Brandenburg","Region":"Nedlitz"}}],"Friends":[],"Trips":[]} - } - - @Test - void testPrimitiveCollection() - { - final String email = "eve@sap.com"; - final Person person = Person.builder().emails(Arrays.asList(email)).build(); - final JsonElement actual = new Gson().toJsonTree(person); - assertThat(actual) - .isEqualTo( - JsonParser - .parseString( - "{\"Emails\":[\"eve@sap.com\"],\"@odata.type\":\"#Trippin.Person\",\"Friends\":[],\"Trips\":[]}")); - } - - @Test - void testDateTimeGson() - { - final OffsetDateTime date1 = LocalDate.of(2020, 2, 20).atStartOfDay().atOffset(ZoneOffset.UTC); - final OffsetDateTime date2 = LocalDate.of(2020, 2, 20).atTime(3, 0).atOffset(ZoneOffset.ofHours(1)); - final Trip trip = Trip.builder().name("Trip1").startsAt(date1).endsAt(date2).build(); - - final String tripJson = new Gson().toJson(trip); - final JsonObject gsonObject = JsonParser.parseString(tripJson).getAsJsonObject(); - - final String startsAt = gsonObject.getAsJsonPrimitive("StartsAt").getAsString(); - assertThat(startsAt).isEqualTo("2020-02-20T00:00:00Z"); - - final String endsAt = gsonObject.getAsJsonPrimitive("EndsAt").getAsString(); - assertThat(endsAt).isEqualTo("2020-02-20T03:00:00+01:00"); - - final Trip tripGson = new Gson().fromJson(tripJson, Trip.class); - assertThat(tripGson).isEqualTo(trip); - assertThat(tripGson.getStartsAt()).isEqualTo(date1); - assertThat(tripGson.getEndsAt()).isEqualTo(date2); - } - - @Test - void testDateTimeJackson() - throws JsonProcessingException - { - final OffsetDateTime date1 = LocalDate.of(2020, 2, 20).atStartOfDay().atOffset(ZoneOffset.UTC); - final OffsetDateTime date2 = LocalDate.of(2020, 2, 20).atTime(3, 0).atOffset(ZoneOffset.ofHours(1)); - final Trip trip = Trip.builder().name("Trip1").startsAt(date1).endsAt(date2).build(); - - final ObjectMapper mapper = new ObjectMapper(); - final String tripJson = mapper.writeValueAsString(trip); - - final String startsAt = mapper.readTree(tripJson).get("StartsAt").textValue(); - assertThat(startsAt).isEqualTo("2020-02-20T00:00:00Z"); - - final String endsAt = mapper.readTree(tripJson).get("EndsAt").textValue(); - assertThat(endsAt).isEqualTo("2020-02-20T03:00:00+01:00"); - - final Trip tripJackson = mapper.readValue(tripJson, Trip.class); - assertThat(tripJackson).isEqualTo(trip); - assertThat(tripJackson.getStartsAt()).isEqualTo(date1); - assertThat(tripJackson.getEndsAt()).isEqualTo(date2); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/README.md b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/README.md deleted file mode 100644 index 453cddc5da..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# TripPin Service - -The tests in this package are based on the TripPin service originally (24.08.2023) found here: - -The modified version can be found here: `/datamodel/odata-v4/odata-v4-core/src/test/resources/unused_trippin.edmx` - -## Regenerating the Model - -Using the latest OData v4 Generator, take the metadata linked above and use a command similar to this: - -```bash -java -jar odata-v4-generator-cli-4.21.0.jar -i /datamodel/odata-v4/odata-v4-core/src/test/resources/ -o /datamodel/odata-v4/odata-v4-core/src/test/java/ -b TripPinRESTierService -f --use-odata-names --service-methods-per-entity-set --sap-copyright-header -p com.sap.cloud.sdk.datamodel.odatav4.referenceservice -``` \ No newline at end of file diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/SearchQueriesTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/SearchQueriesTest.java deleted file mode 100644 index 1e25fce5bb..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/SearchQueriesTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.SearchExpression; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class SearchQueriesTest -{ - @Test - void testGetSearched() - { - final GetAllRequestBuilder search = new DefaultTrippinService().getAllPeople().search("Portland"); - assertThat(search.toRequest().getRelativeUri()).hasParameter("$search", "\"Portland\""); - } - - @Test - void testSpecialCharacters() - { - final GetAllRequestBuilder search = new DefaultTrippinService().getAllPeople().search("Hash #"); - assertThat(search.toRequest().getRelativeUri()).hasParameter("$search", "\"Hash #\""); - } - - @Test - void testGetSearchedPhrase() - { - final GetAllRequestBuilder search = new DefaultTrippinService().getAllPeople().search("United States"); - assertThat(search.toRequest().getRelativeUri()).hasParameter("$search", "\"United States\""); - } - - @Test - void testGetSearchedWithQuotesInString() - { - final GetAllRequestBuilder search = - new DefaultTrippinService().getAllPeople().search("Quoted \"string\""); - - // double quotes are escaped with simple backslash: Quote "string" -> Quoted \"string\" - assertThat(search.toRequest().getRelativeUri()).hasParameter("$search", "\"Quoted \\\"string\\\"\""); - } - - @Test - void testGetSearchedWithBackslashInString() - { - final GetAllRequestBuilder search = new DefaultTrippinService().getAllPeople().search("Escaped \\"); - - // backslashes (=escape character) are escaped with backslashes: Escaped \ -> Escaped \\ - assertThat(search.toRequest().getRelativeUri()).hasParameter("$search", "\"Escaped \\\\\""); - } - - @Test - void testSearchBooleanExpression() - { - - final GetAllRequestBuilder search = - new DefaultTrippinService().getAllPeople().search(SearchExpression.of("termA").or("termB").and("termC")); - - // double quotes are escaped with simple backslash: Quote "string" -> Quoted \"string\" - // (("termA" OR "termB") AND "termC") - assertThat(search.toRequest().getRelativeUri()) - .hasParameter("$search", "((\"termA\" OR \"termB\") AND \"termC\")"); - } - - @Test - void testSearchBooleanNot() - { - - final GetAllRequestBuilder search = - new DefaultTrippinService() - .getAllPeople() - .search(SearchExpression.of("termA").or("termB").and(SearchExpression.of("termC").not())); - - // double quotes are escaped with simple backslash: Quote "string" -> Quoted \"string\" - // (("termA" OR "termB") AND NOT "termC") - assertThat(search.toRequest().getRelativeUri()) - .hasParameter("$search", "((\"termA\" OR \"termB\") AND NOT \"termC\")"); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/SelectNestedComplexPropertiesTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/SelectNestedComplexPropertiesTest.java deleted file mode 100644 index d4a660473f..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/SelectNestedComplexPropertiesTest.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.expression.FilterableBoolean; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.City; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Location; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.PlanItem; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Trip; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; - -class SelectNestedComplexPropertiesTest -{ - @Test - void testSelectComplexProperty() - { - final GetAllRequestBuilder query = - new DefaultTrippinService().getAllPeople().select(Person.FIRST_NAME, Person.LAST_NAME, Person.ADDRESS_INFO); - - final String expectedUnencodedQuery = "FirstName,LastName,AddressInfo"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$select", expectedUnencodedQuery); - } - - @Test - void testSelectNestedComplexProperty() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .select( - Person.FIRST_NAME, - Person.LAST_NAME, - Person.ADDRESS_INFO.select(Location.ADDRESS, Location.CITY)); - - final String expectedUnencodedQuery = "FirstName,LastName,AddressInfo/Address,AddressInfo/City"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$select", expectedUnencodedQuery); - } - - @Test - void testSelectSimplePropertyInNestedComplexProperty() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .select( - Person.FIRST_NAME, - Person.LAST_NAME, - Person.ADDRESS_INFO.select(Location.ADDRESS, Location.CITY.select(City.NAME))); - - final String expectedUnencodedQuery = "FirstName,LastName,AddressInfo/Address,AddressInfo/City/Name"; - - assertThat(query.toRequest().getRelativeUri()).hasParameter("$select", expectedUnencodedQuery); - } - - @Test - void testSelectComplexPropertyInNavigationProperty() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .select( - Person.TO_BEST_FRIEND - .select(Person.FIRST_NAME, Person.ADDRESS_INFO.select(Location.ADDRESS, Location.CITY))); - - assertThat(query.toRequest().getRelativeUri()) - .hasParameter("$expand", "BestFriend($select=FirstName,AddressInfo/Address,AddressInfo/City)"); - } - - @Test - void testAnyInNestedComplexPropertyCollection() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .select(Person.FIRST_NAME, Person.LAST_NAME) - .filter(Person.ADDRESS_INFO.any(Location.ADDRESS.startsWith("Diagon Alley"))); - - assertThat(query.toRequest().getRelativeUri()) - .hasParameter("$select", "FirstName,LastName") - .hasParameter("$filter", "AddressInfo/any(a:startswith(a/Address,'Diagon Alley'))"); - } - - @Test - void testAnyInAnyPropertyCollection() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .filter(Person.TO_FRIENDS.any(Person.ADDRESS_INFO.any(Location.ADDRESS.startsWith("Diagon Alley")))); - - assertThat(query.toRequest().getRelativeUri()) - .hasParameter("$filter", "Friends/any(a:a/AddressInfo/any(b:startswith(b/Address,'Diagon Alley')))"); - } - - @Test - void testAnyBesidesAllPropertyCollection() - { - final GetAllRequestBuilder query = - new DefaultTrippinService() - .getAllPeople() - .filter( - Person.TO_FRIENDS - .any(Person.FIRST_NAME.equalTo("Adam")) - .or(Person.TO_FRIENDS.all(Person.FIRST_NAME.equalTo("Eve")))); - - assertThat(query.toRequest().getRelativeUri()) - .hasParameter( - "$filter", - "(Friends/any(a:(a/FirstName eq 'Adam')) or Friends/all(a:(a/FirstName eq 'Eve')))"); - } - - @Test - void testLambdaParameterLevelsForSameEntity() - { - final FilterableBoolean lvl3 = Person.USER_NAME.equalTo("c1").or(Person.USER_NAME.equalTo("c2")); - - final FilterableBoolean lvl2 = - Person.USER_NAME.equalTo("b1").and(Person.TO_FRIENDS.any(lvl3)).and(Person.USER_NAME.equalTo("b2")); - - final FilterableBoolean lvl1 = - Person.USER_NAME.equalTo("a1").or(Person.TO_FRIENDS.all(lvl2)).or(Person.USER_NAME.equalTo("a2")); - - final FilterableBoolean lvl0 = - Person.USER_NAME.equalTo("1").and(Person.TO_FRIENDS.any(lvl1)).or(Person.USER_NAME.equalTo("2")); - - final GetAllRequestBuilder query = new DefaultTrippinService().getAllPeople().filter(lvl0); - - // Expectation for prefix usage: - // -> 1 - // a -> a1 - // b -> b1 - // c -> c1 - // c -> c2 - // b -> b1 - // a -> b1 - // -> 2 - assertThat(query.toRequest().getRelativeUri()) - .hasParameter( - "$filter", - "(((UserName eq '1') and Friends/any(a:(((a/UserName eq 'a1') or a/Friends/all(b:(((b/UserName eq 'b1') and b/Friends/any(c:((c/UserName eq 'c1') or (c/UserName eq 'c2')))) and (b/UserName eq 'b2')))) or (a/UserName eq 'a2')))) or (UserName eq '2'))"); - } - - @Test - void testLambdaParameterLevelsForDifferentEntities() - { - final FilterableBoolean lvl3 = - PlanItem.CONFIRMATION_CODE.equalTo("c1").or(PlanItem.CONFIRMATION_CODE.equalTo("c2")); - - final FilterableBoolean lvl2 = - Trip.NAME.equalTo("b1").and(Trip.TO_PLAN_ITEMS.any(lvl3)).and(Trip.NAME.equalTo("b2")); - - final FilterableBoolean lvl1 = - Person.USER_NAME.equalTo("a1").or(Person.TO_TRIPS.all(lvl2)).or(Person.USER_NAME.equalTo("a2")); - - final FilterableBoolean lvl0 = - Person.USER_NAME.equalTo("1").and(Person.TO_FRIENDS.any(lvl1)).and(Person.USER_NAME.equalTo("2")); - - final GetAllRequestBuilder query = new DefaultTrippinService().getAllPeople().filter(lvl0); - - // Expectation for prefix usage: - // -> 1 - // a -> a1 - // b -> b1 - // c -> c1 - // c -> c2 - // b -> b1 - // a -> b1 - // -> 2 - assertThat(query.toRequest().getRelativeUri()) - .hasParameter( - "$filter", - "(((UserName eq '1') and Friends/any(a:(((a/UserName eq 'a1') or a/Trips/all(b:(((b/Name eq 'b1') and b/PlanItems/any(c:((c/ConfirmationCode eq 'c1') or (c/ConfirmationCode eq 'c2')))) and (b/Name eq 'b2')))) or (a/UserName eq 'a2')))) and (UserName eq '2'))"); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/TripPinUtility.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/TripPinUtility.java deleted file mode 100644 index 4f94ad6c41..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/TripPinUtility.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.hc.client5.http.classic.methods.HttpGet; -import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; -import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; - -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; - -public class TripPinUtility -{ - public static HttpDestination getDestination() - throws IOException - { - final HttpGet request = new HttpGet("https://services.odata.org/TripPinRESTierService/"); - final CloseableHttpClient client = HttpClientBuilder.create().disableRedirectHandling().build(); - final String serviceUrl = client.executeOpen(null, request, null).getLastHeader("Location").getValue(); - final Matcher tokenLookup = Pattern.compile("\\(S\\((.*?)\\)\\)").matcher(serviceUrl); - - assertThat(tokenLookup.find()).isTrue(); - - return DefaultHttpDestination - .builder("https://services.odata.org/TripPinRESTierService/(S(" + tokenLookup.group(1) + "))") - .build(); - } - - public static HttpDestination getDestinationRW() - throws IOException - { - final HttpGet request = new HttpGet("https://services.odata.org/V4/TripPinServiceRW/"); - final CloseableHttpClient client = HttpClientBuilder.create().disableRedirectHandling().build(); - final String serviceUrl = client.executeOpen(null, request, null).getLastHeader("Location").getValue(); - final Matcher tokenLookup = Pattern.compile("\\(S\\((.*?)\\)\\)").matcher(serviceUrl); - - assertThat(tokenLookup.find()).isTrue(); - - return DefaultHttpDestination - .builder("https://services.odata.org/V4/(S(" + tokenLookup.group(1) + "))") - .build(); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/UnboundActionTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/UnboundActionTest.java deleted file mode 100644 index 797f691de6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/UnboundActionTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; -import com.sap.cloud.sdk.datamodel.odatav4.core.ActionResponseSingle; -import com.sap.cloud.sdk.datamodel.odatav4.core.SingleValueActionRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.TrippinService; - -@Disabled( "Test runs against a v4 reference service on odata.org. Use it only to manually verify behaviour." ) -class UnboundActionTest -{ - - private static final TrippinService service = new DefaultTrippinService(); - private HttpDestination httpDestination; - - @BeforeEach - void configure() - throws IOException - { - httpDestination = TripPinUtility.getDestination(); - } - - @Test - void testActionWithoutParameters() - { - final SingleValueActionRequestBuilder builder = service.resetDataSource(); - final ActionResponseSingle actionResponse = builder.execute(httpDestination); - assertThat(actionResponse).isNotNull(); - assertThat(actionResponse.getResponseStatusCode()).isEqualTo(204); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/UnboundFunctionTest.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/UnboundFunctionTest.java deleted file mode 100644 index d1c66c61f0..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/UnboundFunctionTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination; -import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination; -import com.sap.cloud.sdk.datamodel.odatav4.core.SingleValueFunctionRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.TrippinService; - -@Disabled( "Test runs against a v4 reference service on odata.org. Use it only to manually verify behaviour." ) -class UnboundFunctionTest -{ - - private static final TrippinService service = new DefaultTrippinService(); - private HttpDestination httpDestination; - - @BeforeEach - void configure() - { - httpDestination = DefaultHttpDestination.builder("https://services.odata.org").build(); - } - - @Test - void testFunctionWithoutParameters() - { - final SingleValueFunctionRequestBuilder builder = service.getPersonWithMostFriends(); - final String expected = DefaultTrippinService.DEFAULT_SERVICE_PATH + "/GetPersonWithMostFriends"; - assertThat(builder.toRequest().getRelativeUri()).hasToString(expected); - assertThat(builder.execute(httpDestination)).isInstanceOf(Person.class); - } - - @Test - void testFunctionWithParameters() - { - final SingleValueFunctionRequestBuilder builder = service.getNearestAirport(33.0, -118.0); - final String expected = DefaultTrippinService.DEFAULT_SERVICE_PATH + "/GetNearestAirport(lat=33.0,lon=-118.0)"; - assertThat(builder.toRequest().getRelativeUri()).hasToString(expected); - assertThat(builder.execute(httpDestination)).isInstanceOf(Airport.class); - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Airline.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Airline.java deleted file mode 100644 index 5d3a46baf7..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Airline.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Maps; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntitySet; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.TrippinService; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -/** - *

- * Original entity name from the Odata EDM: Airline - *

- * - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class Airline extends VdmEntity implements VdmEntitySet -{ - - @Getter - private final java.lang.String odataType = "Trippin.Airline"; - /** - * Selector for all available fields of Airline. - * - */ - public final static SimpleProperty ALL_FIELDS = all(); - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: AirlineCode - *

- * - * @return The airlineCode contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "AirlineCode" ) - private java.lang.String airlineCode; - public final static SimpleProperty.String AIRLINE_CODE = - new SimpleProperty.String(Airline.class, "AirlineCode"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @return The name contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Name" ) - private java.lang.String name; - public final static SimpleProperty.String NAME = new SimpleProperty.String(Airline.class, "Name"); - - @Nonnull - @Override - public Class getType() - { - return Airline.class; - } - - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: AirlineCode - *

- * - * @param airlineCode - * The airlineCode to set. - */ - public void setAirlineCode( @Nullable final java.lang.String airlineCode ) - { - rememberChangedField("AirlineCode", this.airlineCode); - this.airlineCode = airlineCode; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @param name - * The name to set. - */ - public void setName( @Nullable final java.lang.String name ) - { - rememberChangedField("Name", this.name); - this.name = name; - } - - @Override - protected java.lang.String getEntityCollection() - { - return "Airlines"; - } - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey entityKey = super.getKey(); - entityKey.addKeyProperty("AirlineCode", getAirlineCode()); - return entityKey; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("AirlineCode", getAirlineCode()); - values.put("Name", getName()); - return values; - } - - @Override - protected void fromMap( final Map inputValues ) - { - final Map values = Maps.newHashMap(inputValues); - // simple properties - { - if( values.containsKey("AirlineCode") ) { - final Object value = values.remove("AirlineCode"); - if( (value == null) || (!value.equals(getAirlineCode())) ) { - setAirlineCode(((java.lang.String) value)); - } - } - if( values.containsKey("Name") ) { - final Object value = values.remove("Name"); - if( (value == null) || (!value.equals(getName())) ) { - setName(((java.lang.String) value)); - } - } - } - // structured properties - { - } - // navigation properties - { - } - super.fromMap(values); - } - - @Override - protected java.lang.String getDefaultServicePath() - { - return TrippinService.DEFAULT_SERVICE_PATH; - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Airport.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Airport.java deleted file mode 100644 index f63591d32a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Airport.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Maps; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntitySet; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.TrippinService; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -/** - *

- * Original entity name from the Odata EDM: Airport - *

- * - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class Airport extends VdmEntity implements VdmEntitySet -{ - - @Getter - private final java.lang.String odataType = "Trippin.Airport"; - /** - * Selector for all available fields of Airport. - * - */ - public final static SimpleProperty ALL_FIELDS = all(); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @return The name contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Name" ) - private java.lang.String name; - public final static SimpleProperty.String NAME = new SimpleProperty.String(Airport.class, "Name"); - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: IcaoCode - *

- * - * @return The icaoCode contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "IcaoCode" ) - private java.lang.String icaoCode; - public final static SimpleProperty.String ICAO_CODE = - new SimpleProperty.String(Airport.class, "IcaoCode"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: IataCode - *

- * - * @return The iataCode contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "IataCode" ) - private java.lang.String iataCode; - public final static SimpleProperty.String IATA_CODE = - new SimpleProperty.String(Airport.class, "IataCode"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Location - *

- * - * @return The location contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Location" ) - private AirportLocation location; - /** - * Use with available request builders to apply the Location complex property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single LOCATION = - new com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single( - Airport.class, - "Location", - AirportLocation.class); - - @Nonnull - @Override - public Class getType() - { - return Airport.class; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @param name - * The name to set. - */ - public void setName( @Nullable final java.lang.String name ) - { - rememberChangedField("Name", this.name); - this.name = name; - } - - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: IcaoCode - *

- * - * @param icaoCode - * The icaoCode to set. - */ - public void setIcaoCode( @Nullable final java.lang.String icaoCode ) - { - rememberChangedField("IcaoCode", this.icaoCode); - this.icaoCode = icaoCode; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: IataCode - *

- * - * @param iataCode - * The iataCode to set. - */ - public void setIataCode( @Nullable final java.lang.String iataCode ) - { - rememberChangedField("IataCode", this.iataCode); - this.iataCode = iataCode; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Location - *

- * - * @param location - * The location to set. - */ - public void setLocation( @Nullable final AirportLocation location ) - { - rememberChangedField("Location", this.location); - this.location = location; - } - - @Override - protected java.lang.String getEntityCollection() - { - return "Airports"; - } - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey entityKey = super.getKey(); - entityKey.addKeyProperty("IcaoCode", getIcaoCode()); - return entityKey; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("Name", getName()); - values.put("IcaoCode", getIcaoCode()); - values.put("IataCode", getIataCode()); - values.put("Location", getLocation()); - return values; - } - - @Override - protected void fromMap( final Map inputValues ) - { - final Map values = Maps.newHashMap(inputValues); - // simple properties - { - if( values.containsKey("Name") ) { - final Object value = values.remove("Name"); - if( (value == null) || (!value.equals(getName())) ) { - setName(((java.lang.String) value)); - } - } - if( values.containsKey("IcaoCode") ) { - final Object value = values.remove("IcaoCode"); - if( (value == null) || (!value.equals(getIcaoCode())) ) { - setIcaoCode(((java.lang.String) value)); - } - } - if( values.containsKey("IataCode") ) { - final Object value = values.remove("IataCode"); - if( (value == null) || (!value.equals(getIataCode())) ) { - setIataCode(((java.lang.String) value)); - } - } - } - // structured properties - { - if( values.containsKey("Location") ) { - final Object value = values.remove("Location"); - if( value instanceof Map ) { - if( getLocation() == null ) { - setLocation(new AirportLocation()); - } - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) value); - getLocation().fromMap(inputMap); - } - if( (value == null) && (getLocation() != null) ) { - setLocation(null); - } - } - } - // navigation properties - { - } - super.fromMap(values); - } - - @Override - protected java.lang.String getDefaultServicePath() - { - return TrippinService.DEFAULT_SERVICE_PATH; - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/AirportLocation.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/AirportLocation.java deleted file mode 100644 index 13ae1e0d0c..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/AirportLocation.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Maps; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmComplex; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -/** - *

- * Original complex type name from the Odata EDM: AirportLocation - *

- * - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class AirportLocation extends VdmComplex -{ - - @Getter - private final java.lang.String odataType = "Trippin.AirportLocation"; - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Address - *

- * - * @return The address contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "Address" ) - private java.lang.String address; - public final static com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String ADDRESS = - new com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String( - AirportLocation.class, - "Address"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: City - *

- * - * @return The city contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "City" ) - private City city; - /** - * Use with available request builders to apply the City complex property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single CITY = - new com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single( - AirportLocation.class, - "City", - City.class); - - @Nonnull - @Override - public Class getType() - { - return AirportLocation.class; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("Address", getAddress()); - values.put("City", getCity()); - return values; - } - - @Override - protected void fromMap( final Map inputValues ) - { - final Map values = Maps.newHashMap(inputValues); - // simple properties - { - if( values.containsKey("Address") ) { - final Object value = values.remove("Address"); - if( (value == null) || (!value.equals(getAddress())) ) { - setAddress(((java.lang.String) value)); - } - } - } - // structured properties - { - if( values.containsKey("City") ) { - final Object value = values.remove("City"); - if( value instanceof Map ) { - if( getCity() == null ) { - setCity(new City()); - } - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) value); - getCity().fromMap(inputMap); - } - if( (value == null) && (getCity() != null) ) { - setCity(null); - } - } - } - // navigation properties - { - } - super.fromMap(values); - } - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey entityKey = super.getKey(); - return entityKey; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Address - *

- * - * @param address - * The address to set. - */ - public void setAddress( @Nullable final java.lang.String address ) - { - rememberChangedField("Address", this.address); - this.address = address; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: City - *

- * - * @param city - * The city to set. - */ - public void setCity( @Nullable final City city ) - { - rememberChangedField("City", this.city); - this.city = city; - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Beverage.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Beverage.java deleted file mode 100644 index 61ff3a4dc7..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Beverage.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Maps; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmComplex; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -/** - *

- * Original complex type name from the Odata EDM: Beverage - *

- * - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class Beverage extends VdmComplex -{ - - @Getter - private final java.lang.String odataType = "Trippin.Beverage"; - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @return The name contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "Name" ) - private java.lang.String name; - public final static com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String NAME = - new com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String(Beverage.class, "Name"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: IsAlcoholic - *

- * - * @return The isAlcoholic contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "IsAlcoholic" ) - private java.lang.Boolean isAlcoholic; - public final static com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.Boolean IS_ALCOHOLIC = - new com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.Boolean(Beverage.class, "IsAlcoholic"); - - @Nonnull - @Override - public Class getType() - { - return Beverage.class; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("Name", getName()); - values.put("IsAlcoholic", getIsAlcoholic()); - return values; - } - - @Override - protected void fromMap( final Map inputValues ) - { - final Map values = Maps.newHashMap(inputValues); - // simple properties - { - if( values.containsKey("Name") ) { - final Object value = values.remove("Name"); - if( (value == null) || (!value.equals(getName())) ) { - setName(((java.lang.String) value)); - } - } - if( values.containsKey("IsAlcoholic") ) { - final Object value = values.remove("IsAlcoholic"); - if( (value == null) || (!value.equals(getIsAlcoholic())) ) { - setIsAlcoholic(((java.lang.Boolean) value)); - } - } - } - // structured properties - { - } - // navigation properties - { - } - super.fromMap(values); - } - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey entityKey = super.getKey(); - return entityKey; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @param name - * The name to set. - */ - public void setName( @Nullable final java.lang.String name ) - { - rememberChangedField("Name", this.name); - this.name = name; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: IsAlcoholic - *

- * - * @param isAlcoholic - * The isAlcoholic to set. - */ - public void setIsAlcoholic( @Nullable final java.lang.Boolean isAlcoholic ) - { - rememberChangedField("IsAlcoholic", this.isAlcoholic); - this.isAlcoholic = isAlcoholic; - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/City.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/City.java deleted file mode 100644 index 5398dafc40..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/City.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Maps; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmComplex; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -/** - *

- * Original complex type name from the Odata EDM: City - *

- * - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class City extends VdmComplex -{ - - @Getter - private final java.lang.String odataType = "Trippin.City"; - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @return The name contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "Name" ) - private java.lang.String name; - public final static com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String NAME = - new com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String(City.class, "Name"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: CountryRegion - *

- * - * @return The countryRegion contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "CountryRegion" ) - private java.lang.String countryRegion; - public final static com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String COUNTRY_REGION = - new com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String(City.class, "CountryRegion"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Region - *

- * - * @return The region contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "Region" ) - private java.lang.String region; - public final static com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String REGION = - new com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String(City.class, "Region"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: FamousBeverage - *

- * - * @return The famousBeverage contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "FamousBeverage" ) - private Beverage famousBeverage; - /** - * Use with available request builders to apply the FamousBeverage complex property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single FAMOUS_BEVERAGE = - new com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single( - City.class, - "FamousBeverage", - Beverage.class); - - @Nonnull - @Override - public Class getType() - { - return City.class; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("Name", getName()); - values.put("CountryRegion", getCountryRegion()); - values.put("Region", getRegion()); - values.put("FamousBeverage", getFamousBeverage()); - return values; - } - - @Override - protected void fromMap( final Map inputValues ) - { - final Map values = Maps.newHashMap(inputValues); - // simple properties - { - if( values.containsKey("Name") ) { - final Object value = values.remove("Name"); - if( (value == null) || (!value.equals(getName())) ) { - setName(((java.lang.String) value)); - } - } - if( values.containsKey("CountryRegion") ) { - final Object value = values.remove("CountryRegion"); - if( (value == null) || (!value.equals(getCountryRegion())) ) { - setCountryRegion(((java.lang.String) value)); - } - } - if( values.containsKey("Region") ) { - final Object value = values.remove("Region"); - if( (value == null) || (!value.equals(getRegion())) ) { - setRegion(((java.lang.String) value)); - } - } - } - // structured properties - { - if( values.containsKey("FamousBeverage") ) { - final Object value = values.remove("FamousBeverage"); - if( value instanceof Map ) { - if( getFamousBeverage() == null ) { - setFamousBeverage(new Beverage()); - } - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) value); - getFamousBeverage().fromMap(inputMap); - } - if( (value == null) && (getFamousBeverage() != null) ) { - setFamousBeverage(null); - } - } - } - // navigation properties - { - } - super.fromMap(values); - } - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey entityKey = super.getKey(); - return entityKey; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @param name - * The name to set. - */ - public void setName( @Nullable final java.lang.String name ) - { - rememberChangedField("Name", this.name); - this.name = name; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: CountryRegion - *

- * - * @param countryRegion - * The countryRegion to set. - */ - public void setCountryRegion( @Nullable final java.lang.String countryRegion ) - { - rememberChangedField("CountryRegion", this.countryRegion); - this.countryRegion = countryRegion; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Region - *

- * - * @param region - * The region to set. - */ - public void setRegion( @Nullable final java.lang.String region ) - { - rememberChangedField("Region", this.region); - this.region = region; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: FamousBeverage - *

- * - * @param famousBeverage - * The famousBeverage to set. - */ - public void setFamousBeverage( @Nullable final Beverage famousBeverage ) - { - rememberChangedField("FamousBeverage", this.famousBeverage); - this.famousBeverage = famousBeverage; - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Feature.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Feature.java deleted file mode 100644 index 5d80fdce0c..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Feature.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmEnumDeserializer; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmEnumSerializer; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; - -/** - *

- * Original enum type name from the Odata EDM: Feature - *

- * - */ -@JsonAdapter( GsonVdmAdapterFactory.class ) -@JsonSerialize( using = JacksonVdmEnumSerializer.class ) -@JsonDeserialize( using = JacksonVdmEnumDeserializer.class ) -public enum Feature implements VdmEnum -{ - - /** - * Feature1 - * - */ - FEATURE1("Feature1", 0L), - - /** - * Feature2 - * - */ - FEATURE2("Feature2", 1L), - - /** - * Feature3 - * - */ - FEATURE3("Feature3", 2L), - - /** - * Feature4 - * - */ - FEATURE4("Feature4", 3L); - - private final String name; - private final Long value; - - private Feature( final String enumName, final Long enumValue ) - { - name = enumName; - value = enumValue; - } - - @Override - public String getName() - { - return name; - } - - @Override - public Long getValue() - { - return value; - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Location.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Location.java deleted file mode 100644 index 36a0810f1a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Location.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Maps; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmComplex; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -/** - *

- * Original complex type name from the Odata EDM: Location - *

- * - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class Location extends VdmComplex -{ - - @Getter - private final java.lang.String odataType = "Trippin.Location"; - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Address - *

- * - * @return The address contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "Address" ) - private java.lang.String address; - public final static com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String ADDRESS = - new com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty.String(Location.class, "Address"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: City - *

- * - * @return The city contained in this {@link VdmComplex}. - */ - @Nullable - @ElementName( "City" ) - private City city; - /** - * Use with available request builders to apply the City complex property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single CITY = - new com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single( - Location.class, - "City", - City.class); - - @Nonnull - @Override - public Class getType() - { - return Location.class; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("Address", getAddress()); - values.put("City", getCity()); - return values; - } - - @Override - protected void fromMap( final Map inputValues ) - { - final Map values = Maps.newHashMap(inputValues); - // simple properties - { - if( values.containsKey("Address") ) { - final Object value = values.remove("Address"); - if( (value == null) || (!value.equals(getAddress())) ) { - setAddress(((java.lang.String) value)); - } - } - } - // structured properties - { - if( values.containsKey("City") ) { - final Object value = values.remove("City"); - if( value instanceof Map ) { - if( getCity() == null ) { - setCity(new City()); - } - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) value); - getCity().fromMap(inputMap); - } - if( (value == null) && (getCity() != null) ) { - setCity(null); - } - } - } - // navigation properties - { - } - super.fromMap(values); - } - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey entityKey = super.getKey(); - return entityKey; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Address - *

- * - * @param address - * The address to set. - */ - public void setAddress( @Nullable final java.lang.String address ) - { - rememberChangedField("Address", this.address); - this.address = address; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: City - *

- * - * @param city - * The city to set. - */ - public void setCity( @Nullable final City city ) - { - rememberChangedField("City", this.city); - this.city = city; - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Person.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Person.java deleted file mode 100644 index d9c5b18af3..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Person.java +++ /dev/null @@ -1,1246 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntitySet; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.TrippinService; -import com.sap.cloud.sdk.result.ElementName; - -import io.vavr.control.Option; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -/** - *

- * Original entity name from the Odata EDM: Person - *

- * - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class Person extends VdmEntity implements VdmEntitySet -{ - - @Getter - private final java.lang.String odataType = "Trippin.Person"; - /** - * Selector for all available fields of Person. - * - */ - public final static SimpleProperty ALL_FIELDS = all(); - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: UserName - *

- * - * @return The userName contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "UserName" ) - private java.lang.String userName; - public final static SimpleProperty.String USER_NAME = - new SimpleProperty.String(Person.class, "UserName"); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: FirstName - *

- * - * @return The firstName contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "FirstName" ) - private java.lang.String firstName; - public final static SimpleProperty.String FIRST_NAME = - new SimpleProperty.String(Person.class, "FirstName"); - /** - * Constraints: Nullable, Maximum length: 26 - *

- * Original property name from the Odata EDM: LastName - *

- * - * @return The lastName contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "LastName" ) - private java.lang.String lastName; - public final static SimpleProperty.String LAST_NAME = - new SimpleProperty.String(Person.class, "LastName"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: MiddleName - *

- * - * @return The middleName contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "MiddleName" ) - private java.lang.String middleName; - public final static SimpleProperty.String MIDDLE_NAME = - new SimpleProperty.String(Person.class, "MiddleName"); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: Gender - *

- * - * @return The gender contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Gender" ) - private PersonGender gender; - public final static SimpleProperty.Enum GENDER = - new SimpleProperty.Enum(Person.class, "Gender", "Trippin.PersonGender"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Age - *

- * - * @return The age contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Age" ) - private Long age; - public final static SimpleProperty.NumericInteger AGE = - new SimpleProperty.NumericInteger(Person.class, "Age"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Emails - *

- * - * @return The emails contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Emails" ) - private java.util.Collection emails; - public final static SimpleProperty.Collection EMAILS = - new SimpleProperty.Collection(Person.class, "Emails", java.lang.String.class); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: AddressInfo - *

- * - * @return The addressInfo contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "AddressInfo" ) - private java.util.Collection addressInfo; - /** - * Use with available request builders to apply the AddressInfo complex property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Collection ADDRESS_INFO = - new com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Collection( - Person.class, - "AddressInfo", - Location.class); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: HomeAddress - *

- * - * @return The homeAddress contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "HomeAddress" ) - private Location homeAddress; - /** - * Use with available request builders to apply the HomeAddress complex property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single HOME_ADDRESS = - new com.sap.cloud.sdk.datamodel.odatav4.core.ComplexProperty.Single( - Person.class, - "HomeAddress", - Location.class); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: FavoriteFeature - *

- * - * @return The favoriteFeature contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "FavoriteFeature" ) - private Feature favoriteFeature; - public final static SimpleProperty.Enum FAVORITE_FEATURE = - new SimpleProperty.Enum(Person.class, "FavoriteFeature", "Trippin.Feature"); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: Features - *

- * - * @return The features contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Features" ) - private java.util.Collection features; - public final static SimpleProperty.Collection FEATURES = - new SimpleProperty.Collection(Person.class, "Features", Feature.class); - /** - * Navigation property Friends for Person to multiple Person. - * - */ - @ElementName( "Friends" ) - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private List toFriends; - /** - * Navigation property BestFriend for Person to single Person. - * - */ - @ElementName( "BestFriend" ) - @Nullable - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private Person toBestFriend; - /** - * Navigation property Trips for Person to multiple Trip. - * - */ - @ElementName( "Trips" ) - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private List toTrips; - /** - * Use with available request builders to apply the Friends navigation property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty.Collection TO_FRIENDS = - new com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty.Collection( - Person.class, - "Friends", - Person.class); - /** - * Use with available request builders to apply the BestFriend navigation property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty.Single TO_BEST_FRIEND = - new com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty.Single( - Person.class, - "BestFriend", - Person.class); - /** - * Use with available request builders to apply the Trips navigation property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty.Collection TO_TRIPS = - new com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty.Collection( - Person.class, - "Trips", - Trip.class); - - @Nonnull - @Override - public Class getType() - { - return Person.class; - } - - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: UserName - *

- * - * @param userName - * The userName to set. - */ - public void setUserName( @Nullable final java.lang.String userName ) - { - rememberChangedField("UserName", this.userName); - this.userName = userName; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: FirstName - *

- * - * @param firstName - * The firstName to set. - */ - public void setFirstName( @Nullable final java.lang.String firstName ) - { - rememberChangedField("FirstName", this.firstName); - this.firstName = firstName; - } - - /** - * Constraints: Nullable, Maximum length: 26 - *

- * Original property name from the Odata EDM: LastName - *

- * - * @param lastName - * The lastName to set. - */ - public void setLastName( @Nullable final java.lang.String lastName ) - { - rememberChangedField("LastName", this.lastName); - this.lastName = lastName; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: MiddleName - *

- * - * @param middleName - * The middleName to set. - */ - public void setMiddleName( @Nullable final java.lang.String middleName ) - { - rememberChangedField("MiddleName", this.middleName); - this.middleName = middleName; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: Gender - *

- * - * @param gender - * The gender to set. - */ - public void setGender( @Nullable final PersonGender gender ) - { - rememberChangedField("Gender", this.gender); - this.gender = gender; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Age - *

- * - * @param age - * The age to set. - */ - public void setAge( @Nullable final Long age ) - { - rememberChangedField("Age", this.age); - this.age = age; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Emails - *

- * - * @param emails - * The emails to set. - */ - public void setEmails( @Nullable final java.util.Collection emails ) - { - rememberChangedField("Emails", this.emails); - this.emails = emails; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: AddressInfo - *

- * - * @param addressInfo - * The addressInfo to set. - */ - public void setAddressInfo( @Nullable final java.util.Collection addressInfo ) - { - rememberChangedField("AddressInfo", this.addressInfo); - this.addressInfo = addressInfo; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: HomeAddress - *

- * - * @param homeAddress - * The homeAddress to set. - */ - public void setHomeAddress( @Nullable final Location homeAddress ) - { - rememberChangedField("HomeAddress", this.homeAddress); - this.homeAddress = homeAddress; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: FavoriteFeature - *

- * - * @param favoriteFeature - * The favoriteFeature to set. - */ - public void setFavoriteFeature( @Nullable final Feature favoriteFeature ) - { - rememberChangedField("FavoriteFeature", this.favoriteFeature); - this.favoriteFeature = favoriteFeature; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: Features - *

- * - * @param features - * The features to set. - */ - public void setFeatures( @Nullable final java.util.Collection features ) - { - rememberChangedField("Features", this.features); - this.features = features; - } - - @Override - protected java.lang.String getEntityCollection() - { - return "People"; - } - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey entityKey = super.getKey(); - entityKey.addKeyProperty("UserName", getUserName()); - return entityKey; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("UserName", getUserName()); - values.put("FirstName", getFirstName()); - values.put("LastName", getLastName()); - values.put("MiddleName", getMiddleName()); - values.put("Gender", getGender()); - values.put("Age", getAge()); - values.put("Emails", getEmails()); - values.put("AddressInfo", getAddressInfo()); - values.put("HomeAddress", getHomeAddress()); - values.put("FavoriteFeature", getFavoriteFeature()); - values.put("Features", getFeatures()); - return values; - } - - @Override - protected void fromMap( final Map inputValues ) - { - final Map values = Maps.newHashMap(inputValues); - // simple properties - { - if( values.containsKey("UserName") ) { - final Object value = values.remove("UserName"); - if( (value == null) || (!value.equals(getUserName())) ) { - setUserName(((java.lang.String) value)); - } - } - if( values.containsKey("FirstName") ) { - final Object value = values.remove("FirstName"); - if( (value == null) || (!value.equals(getFirstName())) ) { - setFirstName(((java.lang.String) value)); - } - } - if( values.containsKey("LastName") ) { - final Object value = values.remove("LastName"); - if( (value == null) || (!value.equals(getLastName())) ) { - setLastName(((java.lang.String) value)); - } - } - if( values.containsKey("MiddleName") ) { - final Object value = values.remove("MiddleName"); - if( (value == null) || (!value.equals(getMiddleName())) ) { - setMiddleName(((java.lang.String) value)); - } - } - if( values.containsKey("Gender") ) { - final Object value = values.remove("Gender"); - if( (value instanceof java.lang.String) || (value == null) ) { - final PersonGender gender = VdmEnum.getConstant(PersonGender.class, ((java.lang.String) value)); - if( !Objects.equals(gender, getGender()) ) { - setGender(gender); - } - } - } - if( values.containsKey("Age") ) { - final Object value = values.remove("Age"); - if( (value == null) || (!value.equals(getAge())) ) { - setAge(((Long) value)); - } - } - if( values.containsKey("Emails") ) { - final Object value = values.remove("Emails"); - if( value instanceof Iterable ) { - final LinkedList emails = new LinkedList(); - for( Object item : ((Iterable) value) ) { - emails.add(((java.lang.String) item)); - } - setEmails(emails); - } - } - if( values.containsKey("FavoriteFeature") ) { - final Object value = values.remove("FavoriteFeature"); - if( (value instanceof java.lang.String) || (value == null) ) { - final Feature favoriteFeature = VdmEnum.getConstant(Feature.class, ((java.lang.String) value)); - if( !Objects.equals(favoriteFeature, getFavoriteFeature()) ) { - setFavoriteFeature(favoriteFeature); - } - } - } - if( values.containsKey("Features") ) { - final Object value = values.remove("Features"); - if( (value == null) && (getFeatures() != null) ) { - setFeatures(null); - } - if( value instanceof Iterable ) { - final LinkedList features = new LinkedList(); - for( Object item : ((Iterable) value) ) { - if( item instanceof java.lang.String ) { - final Feature enumConstant = VdmEnum.getConstant(Feature.class, ((java.lang.String) item)); - features.add(enumConstant); - } - } - if( !Objects.equals(features, getFeatures()) ) { - setFeatures(features); - } - } - } - } - // structured properties - { - if( values.containsKey("AddressInfo") ) { - final Object value = values.remove("AddressInfo"); - if( value instanceof Iterable ) { - final LinkedList addressInfo = new LinkedList(); - for( Object properties : ((Iterable) value) ) { - if( properties instanceof Map ) { - final Location item = new Location(); - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) value); - item.fromMap(inputMap); - addressInfo.add(item); - } - } - setAddressInfo(addressInfo); - } - if( (value == null) && (getAddressInfo() != null) ) { - setAddressInfo(null); - } - } - if( values.containsKey("HomeAddress") ) { - final Object value = values.remove("HomeAddress"); - if( value instanceof Map ) { - if( getHomeAddress() == null ) { - setHomeAddress(new Location()); - } - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) value); - getHomeAddress().fromMap(inputMap); - } - if( (value == null) && (getHomeAddress() != null) ) { - setHomeAddress(null); - } - } - } - // navigation properties - { - if( (values).containsKey("Friends") ) { - final Object value = (values).remove("Friends"); - if( value instanceof Iterable ) { - if( toFriends == null ) { - toFriends = Lists.newArrayList(); - } else { - toFriends = Lists.newArrayList(toFriends); - } - int i = 0; - for( Object item : ((Iterable) value) ) { - if( !(item instanceof Map) ) { - continue; - } - Person entity; - if( toFriends.size() > i ) { - entity = toFriends.get(i); - } else { - entity = new Person(); - toFriends.add(entity); - } - i = (i + 1); - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) item); - entity.fromMap(inputMap); - } - } - } - if( (values).containsKey("BestFriend") ) { - final Object value = (values).remove("BestFriend"); - if( value instanceof Map ) { - if( toBestFriend == null ) { - toBestFriend = new Person(); - } - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) value); - toBestFriend.fromMap(inputMap); - } - } - if( (values).containsKey("Trips") ) { - final Object value = (values).remove("Trips"); - if( value instanceof Iterable ) { - if( toTrips == null ) { - toTrips = Lists.newArrayList(); - } else { - toTrips = Lists.newArrayList(toTrips); - } - int i = 0; - for( Object item : ((Iterable) value) ) { - if( !(item instanceof Map) ) { - continue; - } - Trip entity; - if( toTrips.size() > i ) { - entity = toTrips.get(i); - } else { - entity = new Trip(); - toTrips.add(entity); - } - i = (i + 1); - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) item); - entity.fromMap(inputMap); - } - } - } - } - super.fromMap(values); - } - - @Override - protected java.lang.String getDefaultServicePath() - { - return TrippinService.DEFAULT_SERVICE_PATH; - } - - @Nonnull - @Override - protected Map toMapOfNavigationProperties() - { - final Map values = super.toMapOfNavigationProperties(); - if( toFriends != null ) { - (values).put("Friends", toFriends); - } - if( toBestFriend != null ) { - (values).put("BestFriend", toBestFriend); - } - if( toTrips != null ) { - (values).put("Trips", toTrips); - } - return values; - } - - /** - * Retrieval of associated Person entities (one to many). This corresponds to the OData navigation property - * Friends. - *

- * If the navigation property for an entity Person has not been resolved yet, this method will not - * query further information. Instead its Option result state will be empty. - * - * @return If the information for navigation property Friends is already loaded, the result will contain the - * Person entities. If not, an Option with result state empty is returned. - */ - @Nonnull - public Option> getFriendsIfPresent() - { - return Option.of(toFriends); - } - - /** - * Overwrites the list of associated Person entities for the loaded navigation property Friends. - *

- * If the navigation property Friends of a queried Person is operated lazily, an ODataException - * can be thrown in case of an OData query error. - *

- * Please note: Lazy loading of OData entity associations is the process of asynchronous retrieval and - * persisting of items from a navigation property. If a lazy property is requested by the application for the - * first time and it has not yet been loaded, an OData query will be run in order to load the missing information - * and its result will get cached for future invocations. - * - * @param value - * List of Person entities. - */ - public void setFriends( @Nonnull final List value ) - { - if( toFriends == null ) { - toFriends = Lists.newArrayList(); - } - toFriends.clear(); - toFriends.addAll(value); - } - - /** - * Adds elements to the list of associated Person entities. This corresponds to the OData navigation property - * Friends. - *

- * If the navigation property Friends of a queried Person is operated lazily, an ODataException - * can be thrown in case of an OData query error. - *

- * Please note: Lazy loading of OData entity associations is the process of asynchronous retrieval and - * persisting of items from a navigation property. If a lazy property is requested by the application for the - * first time and it has not yet been loaded, an OData query will be run in order to load the missing information - * and its result will get cached for future invocations. - * - * @param entity - * Array of Person entities. - */ - public void addFriends( Person... entity ) - { - if( toFriends == null ) { - toFriends = Lists.newArrayList(); - } - toFriends.addAll(Lists.newArrayList(entity)); - } - - /** - * Retrieval of associated Person entity (one to one). This corresponds to the OData navigation property - * BestFriend. - *

- * If the navigation property for an entity Person has not been resolved yet, this method will not - * query further information. Instead its Option result state will be empty. - * - * @return If the information for navigation property BestFriend is already loaded, the result will contain - * the Person entity. If not, an Option with result state empty is - * returned. - */ - @Nonnull - public Option getBestFriendIfPresent() - { - return Option.of(toBestFriend); - } - - /** - * Overwrites the associated Person entity for the loaded navigation property BestFriend. - * - * @param value - * New Person entity. - */ - public void setBestFriend( final Person value ) - { - toBestFriend = value; - } - - /** - * Retrieval of associated Trip entities (one to many). This corresponds to the OData navigation property - * Trips. - *

- * If the navigation property for an entity Person has not been resolved yet, this method will not - * query further information. Instead its Option result state will be empty. - * - * @return If the information for navigation property Trips is already loaded, the result will contain the - * Trip entities. If not, an Option with result state empty is returned. - */ - @Nonnull - public Option> getTripsIfPresent() - { - return Option.of(toTrips); - } - - /** - * Overwrites the list of associated Trip entities for the loaded navigation property Trips. - *

- * If the navigation property Trips of a queried Person is operated lazily, an ODataException - * can be thrown in case of an OData query error. - *

- * Please note: Lazy loading of OData entity associations is the process of asynchronous retrieval and - * persisting of items from a navigation property. If a lazy property is requested by the application for the - * first time and it has not yet been loaded, an OData query will be run in order to load the missing information - * and its result will get cached for future invocations. - * - * @param value - * List of Trip entities. - */ - public void setTrips( @Nonnull final List value ) - { - if( toTrips == null ) { - toTrips = Lists.newArrayList(); - } - toTrips.clear(); - toTrips.addAll(value); - } - - /** - * Adds elements to the list of associated Trip entities. This corresponds to the OData navigation property - * Trips. - *

- * If the navigation property Trips of a queried Person is operated lazily, an ODataException - * can be thrown in case of an OData query error. - *

- * Please note: Lazy loading of OData entity associations is the process of asynchronous retrieval and - * persisting of items from a navigation property. If a lazy property is requested by the application for the - * first time and it has not yet been loaded, an OData query will be run in order to load the missing information - * and its result will get cached for future invocations. - * - * @param entity - * Array of Trip entities. - */ - public void addTrips( Trip... entity ) - { - if( toTrips == null ) { - toTrips = Lists.newArrayList(); - } - toTrips.addAll(Lists.newArrayList(entity)); - } - - /** - * Function that can be applied to any entity object of this class. - *

- * - * @return Function object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyFunction(thisFunction)} API. - */ - @Nonnull - public static - com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToSingle - getFavoriteAirline() - { - final Map parameters = Collections.emptyMap(); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToSingle( - Person.class, - Airline.class, - "Trippin.GetFavoriteAirline", - parameters); - } - - /** - * Function that can be applied to any entity object of this class. - *

- * - * @param userName - * Constraints: Not nullable - *

- * Original parameter name from the Odata EDM: userName - *

- * @return Function object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyFunction(thisFunction)} API. - */ - @Nonnull - public static - com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToCollection - getFriendsTrips( @Nonnull final java.lang.String userName ) - { - final Map parameters = new HashMap(); - parameters.put("userName", userName); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToCollection( - Person.class, - Trip.class, - "Trippin.GetFriendsTrips", - parameters); - } - - /** - * Function that can be applied to any entity object of this class. - *

- * - * @return Function object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyFunction(thisFunction)} API. - */ - @Nonnull - public static com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToSingle isHappy() - { - final Map parameters = Collections.emptyMap(); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToSingle( - Person.class, - Boolean.class, - "Trippin.IsHappy", - parameters); - } - - /** - * Function that can be applied to any entity object of this class. - *

- * - * @param really - * Constraints: Nullable - *

- * Original parameter name from the Odata EDM: really - *

- * @return Function object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyFunction(thisFunction)} API. - */ - @Nonnull - public static com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToSingle isHappy( - @Nullable final Boolean really ) - { - final Map parameters = new HashMap(); - parameters.put("really", really); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToSingle( - Person.class, - Boolean.class, - "Trippin.IsHappy", - parameters); - } - - /** - * Function that can be applied to a collection of entities of this class. - *

- * - * @return Function object prepared with the given parameters to be applied to a collection of entities of this - * class. - *

- * To execute it use the {@code service.forEntity(entity).applyFunction(thisFunction)} API. - */ - @Nonnull - public static - com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.CollectionToSingle - areAllFriends() - { - final Map parameters = Collections.emptyMap(); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.CollectionToSingle( - Person.class, - Boolean.class, - "Trippin.AreAllFriends", - parameters); - } - - /** - * Function that can be applied to any entity object of this class. - *

- * - * @return Function object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyFunction(thisFunction)} API. - */ - @Nonnull - public static - com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToSingleEntity.Composable - worstFriend() - { - final Map parameters = Collections.emptyMap(); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToSingleEntity.Composable( - Person.class, - Person.class, - "Trippin.WorstFriend", - parameters); - } - - /** - * Function that can be applied to a collection of entities of this class. - *

- * - * @return Function object prepared with the given parameters to be applied to a collection of entities of this - * class. - *

- * To execute it use the {@code service.forEntity(entity).applyFunction(thisFunction)} API. - */ - @Nonnull - public static - com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.CollectionToSingleEntity.Composable - mostPopularPerson() - { - final Map parameters = Collections.emptyMap(); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.CollectionToSingleEntity.Composable( - Person.class, - Person.class, - "Trippin.MostPopularPerson", - parameters); - } - - /** - * Function that can be applied to a collection of entities of this class. - *

- * - * @return Function object prepared with the given parameters to be applied to a collection of entities of this - * class. - *

- * To execute it use the {@code service.forEntity(entity).applyFunction(thisFunction)} API. - */ - @Nonnull - public static - com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.CollectionToCollectionEntity.Composable - mostPopularPersons() - { - final Map parameters = Collections.emptyMap(); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.CollectionToCollectionEntity.Composable( - Person.class, - Person.class, - "Trippin.MostPopularPersons", - parameters); - } - - /** - * Action that can be applied to any entity object of this class. - *

- * - * @param lastName - * Constraints: Not nullable - *

- * Original parameter name from the Odata EDM: lastName - *

- * @return Action object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyAction(thisAction)} API. - */ - @Nonnull - public static com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToSingle updateLastName( - @Nonnull final java.lang.String lastName ) - { - final Map parameters = new HashMap(); - parameters.put("lastName", lastName); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToSingle( - Person.class, - Boolean.class, - "Trippin.UpdateLastName", - parameters); - } - - /** - * Action that can be applied to any entity object of this class. - *

- * - * @param tripId - * Constraints: Not nullable - *

- * Original parameter name from the Odata EDM: tripId - *

- * @param userName - * Constraints: Not nullable - *

- * Original parameter name from the Odata EDM: userName - *

- * @return Action object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyAction(thisAction)} API. - */ - @Nonnull - public static - com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToSingle - shareTrip( @Nonnull final java.lang.String userName, @Nonnull final Integer tripId ) - { - final Map parameters = new HashMap(); - parameters.put("userName", userName); - parameters.put("tripId", tripId); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToSingle( - Person.class, - Void.class, - "Trippin.ShareTrip", - parameters); - } - - /** - * Action that can be applied to any entity object of this class. - *

- * - * @return Action object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyAction(thisAction)} API. - */ - @Nonnull - public static com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToSingle makeHappy() - { - final Map parameters = Collections.emptyMap(); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToSingle( - Person.class, - Void.class, - "Trippin.MakeHappy", - parameters); - } - - /** - * Action that can be applied to any entity object of this class. - *

- * - * @param very - * Constraints: Nullable - *

- * Original parameter name from the Odata EDM: very - *

- * @return Action object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyAction(thisAction)} API. - */ - @Nonnull - public static com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToSingle makeUnhappy( - @Nullable final Boolean very ) - { - final Map parameters = new HashMap(); - parameters.put("very", very); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToSingle( - Person.class, - Person.class, - "Trippin.MakeUnhappy", - parameters); - } - - /** - * Action that can be applied to a collection of entities of this class. - *

- * - * @return Action object prepared with the given parameters to be applied to a collection of entities of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyAction(thisAction)} API. - */ - @Nonnull - public static com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.CollectionToSingle makeAllHappy() - { - final Map parameters = Collections.emptyMap(); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.CollectionToSingle( - Person.class, - Void.class, - "Trippin.MakeAllHappy", - parameters); - } - - /** - * Action that can be applied to any entity object of this class. - *

- * - * @param subject - * Constraints: Nullable - *

- * Original parameter name from the Odata EDM: subject - *

- * @return Action object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyAction(thisAction)} API. - */ - @Nonnull - public static com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToCollection sendMail( - @Nullable final java.lang.String subject ) - { - final Map parameters = new HashMap(); - parameters.put("subject", subject); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundAction.SingleToCollection( - Person.class, - Person.class, - "Trippin.sendMail", - parameters); - } - - /** - * Helper class to allow for fluent creation of Person instances. - * - */ - public final static class PersonBuilder - { - - private List toFriends = Lists.newArrayList(); - private Person toBestFriend; - private List toTrips = Lists.newArrayList(); - - private Person.PersonBuilder toFriends( final List value ) - { - toFriends.addAll(value); - return this; - } - - /** - * Navigation property Friends for Person to multiple Person. - * - * @param value - * The Persons to build this Person with. - * @return This Builder to allow for a fluent interface. - */ - @Nonnull - public Person.PersonBuilder friends( Person... value ) - { - return toFriends(Lists.newArrayList(value)); - } - - private Person.PersonBuilder toBestFriend( final Person value ) - { - toBestFriend = value; - return this; - } - - /** - * Navigation property BestFriend for Person to single Person. - * - * @param value - * The Person to build this Person with. - * @return This Builder to allow for a fluent interface. - */ - @Nonnull - public Person.PersonBuilder bestFriend( final Person value ) - { - return toBestFriend(value); - } - - private Person.PersonBuilder toTrips( final List value ) - { - toTrips.addAll(value); - return this; - } - - /** - * Navigation property Trips for Person to multiple Trip. - * - * @param value - * The Trips to build this Person with. - * @return This Builder to allow for a fluent interface. - */ - @Nonnull - public Person.PersonBuilder trips( Trip... value ) - { - return toTrips(Lists.newArrayList(value)); - } - - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/PersonGender.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/PersonGender.java deleted file mode 100644 index d581b02c5d..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/PersonGender.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmEnumDeserializer; -import com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmEnumSerializer; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEnum; - -/** - *

- * Original enum type name from the Odata EDM: PersonGender - *

- * - */ -@JsonAdapter( GsonVdmAdapterFactory.class ) -@JsonSerialize( using = JacksonVdmEnumSerializer.class ) -@JsonDeserialize( using = JacksonVdmEnumDeserializer.class ) -public enum PersonGender implements VdmEnum -{ - - /** - * Male - * - */ - MALE("Male", 0L), - - /** - * Female - * - */ - FEMALE("Female", 1L), - - /** - * Unknown - * - */ - UNKNOWN("Unknown", 2L); - - private final String name; - private final Long value; - - private PersonGender( final String enumName, final Long enumValue ) - { - name = enumName; - value = enumValue; - } - - @Override - public String getName() - { - return name; - } - - @Override - public Long getValue() - { - return value; - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/PlanItem.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/PlanItem.java deleted file mode 100644 index 731f0b86aa..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/PlanItem.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import java.math.BigDecimal; -import java.time.OffsetDateTime; -import java.util.Map; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Maps; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.result.ElementName; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -/** - *

- * Original entity name from the Odata EDM: PlanItem - *

- * - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class PlanItem extends VdmEntity -{ - - @Getter - private final java.lang.String odataType = "Trippin.PlanItem"; - /** - * Selector for all available fields of PlanItem. - * - */ - public final static SimpleProperty ALL_FIELDS = all(); - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: PlanItemId - *

- * - * @return The planItemId contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "PlanItemId" ) - private Integer planItemId; - public final static SimpleProperty.NumericInteger PLAN_ITEM_ID = - new SimpleProperty.NumericInteger(PlanItem.class, "PlanItemId"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: ConfirmationCode - *

- * - * @return The confirmationCode contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "ConfirmationCode" ) - private java.lang.String confirmationCode; - public final static SimpleProperty.String CONFIRMATION_CODE = - new SimpleProperty.String(PlanItem.class, "ConfirmationCode"); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: StartsAt - *

- * - * @return The startsAt contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "StartsAt" ) - private OffsetDateTime startsAt; - public final static SimpleProperty.DateTime STARTS_AT = - new SimpleProperty.DateTime(PlanItem.class, "StartsAt"); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: EndsAt - *

- * - * @return The endsAt contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "EndsAt" ) - private OffsetDateTime endsAt; - public final static SimpleProperty.DateTime ENDS_AT = - new SimpleProperty.DateTime(PlanItem.class, "EndsAt"); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: Duration - *

- * - * @return The duration contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Duration" ) - private BigDecimal duration; - public final static SimpleProperty.Duration DURATION = - new SimpleProperty.Duration(PlanItem.class, "Duration"); - - @Nonnull - @Override - public Class getType() - { - return PlanItem.class; - } - - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: PlanItemId - *

- * - * @param planItemId - * The planItemId to set. - */ - public void setPlanItemId( @Nullable final Integer planItemId ) - { - rememberChangedField("PlanItemId", this.planItemId); - this.planItemId = planItemId; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: ConfirmationCode - *

- * - * @param confirmationCode - * The confirmationCode to set. - */ - public void setConfirmationCode( @Nullable final java.lang.String confirmationCode ) - { - rememberChangedField("ConfirmationCode", this.confirmationCode); - this.confirmationCode = confirmationCode; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: StartsAt - *

- * - * @param startsAt - * The startsAt to set. - */ - public void setStartsAt( @Nullable final OffsetDateTime startsAt ) - { - rememberChangedField("StartsAt", this.startsAt); - this.startsAt = startsAt; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: EndsAt - *

- * - * @param endsAt - * The endsAt to set. - */ - public void setEndsAt( @Nullable final OffsetDateTime endsAt ) - { - rememberChangedField("EndsAt", this.endsAt); - this.endsAt = endsAt; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: Duration - *

- * - * @param duration - * The duration to set. - */ - public void setDuration( @Nullable final BigDecimal duration ) - { - rememberChangedField("Duration", this.duration); - this.duration = duration; - } - - @Override - protected java.lang.String getEntityCollection() - { - return "PlanItems"; - } - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey entityKey = super.getKey(); - entityKey.addKeyProperty("PlanItemId", getPlanItemId()); - return entityKey; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("PlanItemId", getPlanItemId()); - values.put("ConfirmationCode", getConfirmationCode()); - values.put("StartsAt", getStartsAt()); - values.put("EndsAt", getEndsAt()); - values.put("Duration", getDuration()); - return values; - } - - @Override - protected void fromMap( final Map inputValues ) - { - final Map values = Maps.newHashMap(inputValues); - // simple properties - { - if( values.containsKey("PlanItemId") ) { - final Object value = values.remove("PlanItemId"); - if( (value == null) || (!value.equals(getPlanItemId())) ) { - setPlanItemId(((Integer) value)); - } - } - if( values.containsKey("ConfirmationCode") ) { - final Object value = values.remove("ConfirmationCode"); - if( (value == null) || (!value.equals(getConfirmationCode())) ) { - setConfirmationCode(((java.lang.String) value)); - } - } - if( values.containsKey("StartsAt") ) { - final Object value = values.remove("StartsAt"); - if( (value == null) || (!value.equals(getStartsAt())) ) { - setStartsAt(((OffsetDateTime) value)); - } - } - if( values.containsKey("EndsAt") ) { - final Object value = values.remove("EndsAt"); - if( (value == null) || (!value.equals(getEndsAt())) ) { - setEndsAt(((OffsetDateTime) value)); - } - } - if( values.containsKey("Duration") ) { - final Object value = values.remove("Duration"); - if( (value == null) || (!value.equals(getDuration())) ) { - setDuration(((BigDecimal) value)); - } - } - } - // structured properties - { - } - // navigation properties - { - } - super.fromMap(values); - } - - @Nonnull - @Override - protected Map toMapOfNavigationProperties() - { - final Map values = super.toMapOfNavigationProperties(); - return values; - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Trip.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Trip.java deleted file mode 100644 index 729f984bec..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/namespaces/trippin/Trip.java +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin; - -import java.time.OffsetDateTime; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.gson.annotations.JsonAdapter; -import com.sap.cloud.sdk.datamodel.odata.client.request.ODataEntityKey; -import com.sap.cloud.sdk.datamodel.odatav4.core.SimpleProperty; -import com.sap.cloud.sdk.datamodel.odatav4.core.VdmEntity; -import com.sap.cloud.sdk.result.ElementName; - -import io.vavr.control.Option; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -/** - *

- * Original entity name from the Odata EDM: Trip - *

- * - */ -@Builder -@Data -@NoArgsConstructor -@AllArgsConstructor -@ToString( doNotUseGetters = true, callSuper = true ) -@EqualsAndHashCode( doNotUseGetters = true, callSuper = true ) -@JsonAdapter( com.sap.cloud.sdk.datamodel.odatav4.adapter.GsonVdmAdapterFactory.class ) -@JsonSerialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectSerializer.class ) -@JsonDeserialize( using = com.sap.cloud.sdk.datamodel.odatav4.adapter.JacksonVdmObjectDeserializer.class ) -public class Trip extends VdmEntity -{ - - @Getter - private final java.lang.String odataType = "Trippin.Trip"; - /** - * Selector for all available fields of Trip. - * - */ - public final static SimpleProperty ALL_FIELDS = all(); - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: TripId - *

- * - * @return The tripId contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "TripId" ) - private Integer tripId; - public final static SimpleProperty.NumericInteger TRIP_ID = - new SimpleProperty.NumericInteger(Trip.class, "TripId"); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: ShareId - *

- * - * @return The shareId contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "ShareId" ) - private UUID shareId; - public final static SimpleProperty.Guid SHARE_ID = new SimpleProperty.Guid(Trip.class, "ShareId"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @return The name contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Name" ) - private java.lang.String name; - public final static SimpleProperty.String NAME = new SimpleProperty.String(Trip.class, "Name"); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: Budget - *

- * - * @return The budget contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Budget" ) - private Float budget; - public final static SimpleProperty.NumericDecimal BUDGET = - new SimpleProperty.NumericDecimal(Trip.class, "Budget"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Description - *

- * - * @return The description contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Description" ) - private java.lang.String description; - public final static SimpleProperty.String DESCRIPTION = - new SimpleProperty.String(Trip.class, "Description"); - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Tags - *

- * - * @return The tags contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "Tags" ) - private java.util.Collection tags; - public final static SimpleProperty.Collection TAGS = - new SimpleProperty.Collection(Trip.class, "Tags", java.lang.String.class); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: StartsAt - *

- * - * @return The startsAt contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "StartsAt" ) - private OffsetDateTime startsAt; - public final static SimpleProperty.DateTime STARTS_AT = - new SimpleProperty.DateTime(Trip.class, "StartsAt"); - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: EndsAt - *

- * - * @return The endsAt contained in this {@link VdmEntity}. - */ - @Nullable - @ElementName( "EndsAt" ) - private OffsetDateTime endsAt; - public final static SimpleProperty.DateTime ENDS_AT = new SimpleProperty.DateTime(Trip.class, "EndsAt"); - /** - * Navigation property PlanItems for Trip to multiple PlanItem. - * - */ - @ElementName( "PlanItems" ) - @Getter( AccessLevel.NONE ) - @Setter( AccessLevel.NONE ) - private List toPlanItems; - /** - * Use with available request builders to apply the PlanItems navigation property to query operations. - * - */ - public final static com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty.Collection TO_PLAN_ITEMS = - new com.sap.cloud.sdk.datamodel.odatav4.core.NavigationProperty.Collection( - Trip.class, - "PlanItems", - PlanItem.class); - - @Nonnull - @Override - public Class getType() - { - return Trip.class; - } - - /** - * (Key Field) Constraints: Not nullable - *

- * Original property name from the Odata EDM: TripId - *

- * - * @param tripId - * The tripId to set. - */ - public void setTripId( @Nullable final Integer tripId ) - { - rememberChangedField("TripId", this.tripId); - this.tripId = tripId; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: ShareId - *

- * - * @param shareId - * The shareId to set. - */ - public void setShareId( @Nullable final UUID shareId ) - { - rememberChangedField("ShareId", this.shareId); - this.shareId = shareId; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Name - *

- * - * @param name - * The name to set. - */ - public void setName( @Nullable final java.lang.String name ) - { - rememberChangedField("Name", this.name); - this.name = name; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: Budget - *

- * - * @param budget - * The budget to set. - */ - public void setBudget( @Nullable final Float budget ) - { - rememberChangedField("Budget", this.budget); - this.budget = budget; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Description - *

- * - * @param description - * The description to set. - */ - public void setDescription( @Nullable final java.lang.String description ) - { - rememberChangedField("Description", this.description); - this.description = description; - } - - /** - * Constraints: Nullable - *

- * Original property name from the Odata EDM: Tags - *

- * - * @param tags - * The tags to set. - */ - public void setTags( @Nullable final java.util.Collection tags ) - { - rememberChangedField("Tags", this.tags); - this.tags = tags; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: StartsAt - *

- * - * @param startsAt - * The startsAt to set. - */ - public void setStartsAt( @Nullable final OffsetDateTime startsAt ) - { - rememberChangedField("StartsAt", this.startsAt); - this.startsAt = startsAt; - } - - /** - * Constraints: Not nullable - *

- * Original property name from the Odata EDM: EndsAt - *

- * - * @param endsAt - * The endsAt to set. - */ - public void setEndsAt( @Nullable final OffsetDateTime endsAt ) - { - rememberChangedField("EndsAt", this.endsAt); - this.endsAt = endsAt; - } - - @Override - protected java.lang.String getEntityCollection() - { - return "Trips"; - } - - @Nonnull - @Override - protected ODataEntityKey getKey() - { - final ODataEntityKey entityKey = super.getKey(); - entityKey.addKeyProperty("TripId", getTripId()); - return entityKey; - } - - @Nonnull - @Override - protected Map toMapOfFields() - { - final Map values = super.toMapOfFields(); - values.put("TripId", getTripId()); - values.put("ShareId", getShareId()); - values.put("Name", getName()); - values.put("Budget", getBudget()); - values.put("Description", getDescription()); - values.put("Tags", getTags()); - values.put("StartsAt", getStartsAt()); - values.put("EndsAt", getEndsAt()); - return values; - } - - @Override - protected void fromMap( final Map inputValues ) - { - final Map values = Maps.newHashMap(inputValues); - // simple properties - { - if( values.containsKey("TripId") ) { - final Object value = values.remove("TripId"); - if( (value == null) || (!value.equals(getTripId())) ) { - setTripId(((Integer) value)); - } - } - if( values.containsKey("ShareId") ) { - final Object value = values.remove("ShareId"); - if( (value == null) || (!value.equals(getShareId())) ) { - setShareId(((UUID) value)); - } - } - if( values.containsKey("Name") ) { - final Object value = values.remove("Name"); - if( (value == null) || (!value.equals(getName())) ) { - setName(((java.lang.String) value)); - } - } - if( values.containsKey("Budget") ) { - final Object value = values.remove("Budget"); - if( (value == null) || (!value.equals(getBudget())) ) { - setBudget(((Float) value)); - } - } - if( values.containsKey("Description") ) { - final Object value = values.remove("Description"); - if( (value == null) || (!value.equals(getDescription())) ) { - setDescription(((java.lang.String) value)); - } - } - if( values.containsKey("Tags") ) { - final Object value = values.remove("Tags"); - if( value instanceof Iterable ) { - final LinkedList tags = new LinkedList(); - for( Object item : ((Iterable) value) ) { - tags.add(((java.lang.String) item)); - } - setTags(tags); - } - } - if( values.containsKey("StartsAt") ) { - final Object value = values.remove("StartsAt"); - if( (value == null) || (!value.equals(getStartsAt())) ) { - setStartsAt(((OffsetDateTime) value)); - } - } - if( values.containsKey("EndsAt") ) { - final Object value = values.remove("EndsAt"); - if( (value == null) || (!value.equals(getEndsAt())) ) { - setEndsAt(((OffsetDateTime) value)); - } - } - } - // structured properties - { - } - // navigation properties - { - if( (values).containsKey("PlanItems") ) { - final Object value = (values).remove("PlanItems"); - if( value instanceof Iterable ) { - if( toPlanItems == null ) { - toPlanItems = Lists.newArrayList(); - } else { - toPlanItems = Lists.newArrayList(toPlanItems); - } - int i = 0; - for( Object item : ((Iterable) value) ) { - if( !(item instanceof Map) ) { - continue; - } - PlanItem entity; - if( toPlanItems.size() > i ) { - entity = toPlanItems.get(i); - } else { - entity = new PlanItem(); - toPlanItems.add(entity); - } - i = (i + 1); - @SuppressWarnings( "unchecked" ) - final Map inputMap = ((Map) item); - entity.fromMap(inputMap); - } - } - } - } - super.fromMap(values); - } - - @Nonnull - @Override - protected Map toMapOfNavigationProperties() - { - final Map values = super.toMapOfNavigationProperties(); - if( toPlanItems != null ) { - (values).put("PlanItems", toPlanItems); - } - return values; - } - - /** - * Retrieval of associated PlanItem entities (one to many). This corresponds to the OData navigation property - * PlanItems. - *

- * If the navigation property for an entity Trip has not been resolved yet, this method will not query - * further information. Instead its Option result state will be empty. - * - * @return If the information for navigation property PlanItems is already loaded, the result will contain - * the PlanItem entities. If not, an Option with result state empty is - * returned. - */ - @Nonnull - public Option> getPlanItemsIfPresent() - { - return Option.of(toPlanItems); - } - - /** - * Overwrites the list of associated PlanItem entities for the loaded navigation property PlanItems. - *

- * If the navigation property PlanItems of a queried Trip is operated lazily, an ODataException - * can be thrown in case of an OData query error. - *

- * Please note: Lazy loading of OData entity associations is the process of asynchronous retrieval and - * persisting of items from a navigation property. If a lazy property is requested by the application for the - * first time and it has not yet been loaded, an OData query will be run in order to load the missing information - * and its result will get cached for future invocations. - * - * @param value - * List of PlanItem entities. - */ - public void setPlanItems( @Nonnull final List value ) - { - if( toPlanItems == null ) { - toPlanItems = Lists.newArrayList(); - } - toPlanItems.clear(); - toPlanItems.addAll(value); - } - - /** - * Adds elements to the list of associated PlanItem entities. This corresponds to the OData navigation - * property PlanItems. - *

- * If the navigation property PlanItems of a queried Trip is operated lazily, an ODataException - * can be thrown in case of an OData query error. - *

- * Please note: Lazy loading of OData entity associations is the process of asynchronous retrieval and - * persisting of items from a navigation property. If a lazy property is requested by the application for the - * first time and it has not yet been loaded, an OData query will be run in order to load the missing information - * and its result will get cached for future invocations. - * - * @param entity - * Array of PlanItem entities. - */ - public void addPlanItems( PlanItem... entity ) - { - if( toPlanItems == null ) { - toPlanItems = Lists.newArrayList(); - } - toPlanItems.addAll(Lists.newArrayList(entity)); - } - - /** - * Function that can be applied to any entity object of this class. - *

- * - * @return Function object prepared with the given parameters to be applied to any entity object of this class. - *

- * To execute it use the {@code service.forEntity(entity).applyFunction(thisFunction)} API. - */ - @Nonnull - public static - com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToCollection - getInvolvedPeople() - { - final Map parameters = Collections.emptyMap(); - return new com.sap.cloud.sdk.datamodel.odatav4.core.BoundFunction.SingleToCollection( - Trip.class, - Person.class, - "Trippin.GetInvolvedPeople", - parameters); - } - - /** - * Helper class to allow for fluent creation of Trip instances. - * - */ - public final static class TripBuilder - { - - private List toPlanItems = Lists.newArrayList(); - - private Trip.TripBuilder toPlanItems( final List value ) - { - toPlanItems.addAll(value); - return this; - } - - /** - * Navigation property PlanItems for Trip to multiple PlanItem. - * - * @param value - * The PlanItems to build this Trip with. - * @return This Builder to allow for a fluent interface. - */ - @Nonnull - public Trip.TripBuilder planItems( PlanItem... value ) - { - return toPlanItems(Lists.newArrayList(value)); - } - - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/services/DefaultTrippinService.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/services/DefaultTrippinService.java deleted file mode 100644 index fe1d480252..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/services/DefaultTrippinService.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odatav4.core.BatchRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.CountRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.CreateRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.DeleteRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.GetByKeyRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.ServiceWithNavigableEntities; -import com.sap.cloud.sdk.datamodel.odatav4.core.SingleValueActionRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.SingleValueFunctionRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.UpdateRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; - -import lombok.Getter; - -/** - *

Details:

- * - * - * - * - * - *
OData Service:trippin
- * - */ -public class DefaultTrippinService implements ServiceWithNavigableEntities, TrippinService -{ - - @Nonnull - @Getter - private final String servicePath; - - /** - * Creates a service using {@link TrippinService#DEFAULT_SERVICE_PATH} to send the requests. - * - */ - public DefaultTrippinService() - { - servicePath = TrippinService.DEFAULT_SERVICE_PATH; - } - - /** - * Creates a service using the provided service path to send the requests. - *

- * Used by the fluent {@link #withServicePath(String)} method. - * - */ - private DefaultTrippinService( @Nonnull final String servicePath ) - { - this.servicePath = servicePath; - } - - @Override - @Nonnull - public DefaultTrippinService withServicePath( @Nonnull final String servicePath ) - { - return new DefaultTrippinService(servicePath); - } - - @Override - @Nonnull - public BatchRequestBuilder batch() - { - return new BatchRequestBuilder(servicePath); - } - - @Override - @Nonnull - public GetAllRequestBuilder getAllPeople() - { - return new GetAllRequestBuilder(servicePath, Person.class, "People"); - } - - @Override - @Nonnull - public CountRequestBuilder countPeople() - { - return new CountRequestBuilder(servicePath, Person.class, "People"); - } - - @Override - @Nonnull - public GetByKeyRequestBuilder getPeopleByKey( final String userName ) - { - final Map key = new HashMap(); - key.put("UserName", userName); - return new GetByKeyRequestBuilder(servicePath, Person.class, key, "People"); - } - - @Override - @Nonnull - public CreateRequestBuilder createPeople( @Nonnull final Person person ) - { - return new CreateRequestBuilder(servicePath, person, "People"); - } - - @Override - @Nonnull - public UpdateRequestBuilder updatePeople( @Nonnull final Person person ) - { - return new UpdateRequestBuilder(servicePath, person, "People"); - } - - @Override - @Nonnull - public DeleteRequestBuilder deletePeople( @Nonnull final Person person ) - { - return new DeleteRequestBuilder(servicePath, person, "People"); - } - - @Override - @Nonnull - public GetAllRequestBuilder getAllAirlines() - { - return new GetAllRequestBuilder(servicePath, Airline.class, "Airlines"); - } - - @Override - @Nonnull - public CountRequestBuilder countAirlines() - { - return new CountRequestBuilder(servicePath, Airline.class, "Airlines"); - } - - @Override - @Nonnull - public GetByKeyRequestBuilder getAirlinesByKey( final String airlineCode ) - { - final Map key = new HashMap(); - key.put("AirlineCode", airlineCode); - return new GetByKeyRequestBuilder(servicePath, Airline.class, key, "Airlines"); - } - - @Override - @Nonnull - public CreateRequestBuilder createAirlines( @Nonnull final Airline airline ) - { - return new CreateRequestBuilder(servicePath, airline, "Airlines"); - } - - @Override - @Nonnull - public UpdateRequestBuilder updateAirlines( @Nonnull final Airline airline ) - { - return new UpdateRequestBuilder(servicePath, airline, "Airlines"); - } - - @Override - @Nonnull - public DeleteRequestBuilder deleteAirlines( @Nonnull final Airline airline ) - { - return new DeleteRequestBuilder(servicePath, airline, "Airlines"); - } - - @Override - @Nonnull - public GetAllRequestBuilder getAllAirports() - { - return new GetAllRequestBuilder(servicePath, Airport.class, "Airports"); - } - - @Override - @Nonnull - public CountRequestBuilder countAirports() - { - return new CountRequestBuilder(servicePath, Airport.class, "Airports"); - } - - @Override - @Nonnull - public GetByKeyRequestBuilder getAirportsByKey( final String icaoCode ) - { - final Map key = new HashMap(); - key.put("IcaoCode", icaoCode); - return new GetByKeyRequestBuilder(servicePath, Airport.class, key, "Airports"); - } - - @Override - @Nonnull - public CreateRequestBuilder createAirports( @Nonnull final Airport airport ) - { - return new CreateRequestBuilder(servicePath, airport, "Airports"); - } - - @Override - @Nonnull - public UpdateRequestBuilder updateAirports( @Nonnull final Airport airport ) - { - return new UpdateRequestBuilder(servicePath, airport, "Airports"); - } - - @Override - @Nonnull - public DeleteRequestBuilder deleteAirports( @Nonnull final Airport airport ) - { - return new DeleteRequestBuilder(servicePath, airport, "Airports"); - } - - @Override - @Nonnull - public SingleValueFunctionRequestBuilder getPersonWithMostFriends() - { - return new SingleValueFunctionRequestBuilder(servicePath, "GetPersonWithMostFriends", Person.class); - } - - @Override - @Nonnull - public - SingleValueFunctionRequestBuilder - getNearestAirport( @Nonnull final Double lat, @Nonnull final Double lon ) - { - final LinkedHashMap parameters = new LinkedHashMap(); - parameters.put("lat", lat); - parameters.put("lon", lon); - return new SingleValueFunctionRequestBuilder( - servicePath, - "GetNearestAirport", - parameters, - Airport.class); - } - - @Override - @Nonnull - public SingleValueActionRequestBuilder resetDataSource() - { - return new SingleValueActionRequestBuilder(servicePath, "ResetDataSource", Void.class); - } - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/services/TrippinService.java b/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/services/TrippinService.java deleted file mode 100644 index 58a2a882d3..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/java/com/sap/cloud/sdk/datamodel/odatav4/referenceservice/services/TrippinService.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Generated by OData VDM code generator of SAP Cloud SDK in version 4.21.0 - */ - -package com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services; - -import javax.annotation.Nonnull; - -import com.sap.cloud.sdk.datamodel.odatav4.core.BatchRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.CountRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.CreateRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.DeleteRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.GetByKeyRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.SingleValueActionRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.SingleValueFunctionRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.core.UpdateRequestBuilder; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport; -import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person; - -/** - *

Details:

- * - * - * - * - * - *
OData Service:trippin
- * - */ -public interface TrippinService -{ - - /** - * If no other path was provided via the {@link #withServicePath(String)} method, this is the default service path - * used to access the endpoint. - * - */ - String DEFAULT_SERVICE_PATH = "/TripPinRESTierServiceTrippin"; - - /** - * Overrides the default service path and returns a new service instance with the specified service path. Also - * adjusts the respective entity URLs. - * - * @param servicePath - * Service path that will override the default. - * @return A new service instance with the specified service path. - */ - @Nonnull - TrippinService withServicePath( @Nonnull final String servicePath ); - - /** - * Creates a batch request builder object. - * - * @return A request builder to handle batch operation on this service. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.BatchRequestBuilder#execute(Destination) execute} method - * on the request builder object. - */ - @Nonnull - BatchRequestBuilder batch(); - - /** - * Fetch multiple {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} - * entities. - * - * @return A request builder to fetch multiple - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entities. - * This request builder allows methods which modify the underlying query to be called before executing the - * query itself. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - GetAllRequestBuilder getAllPeople(); - - /** - * Fetch the number of entries from the - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entity collection - * matching the filter and search expressions. - * - * @return A request builder to fetch the count of - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entities. - * This request builder allows methods which modify the underlying query to be called before executing the - * query itself. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.CountRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - CountRequestBuilder countPeople(); - - /** - * Fetch a single {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} - * entity using key fields. - * - * @param userName - *

- * Constraints: Not nullable - *

- * @return A request builder to fetch a single - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entity - * using key fields. This request builder allows methods which modify the underlying query to be called - * before executing the query itself. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.GetByKeyRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - GetByKeyRequestBuilder getPeopleByKey( final String userName ); - - /** - * Create a new {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entity - * and save it to the S/4HANA system. - * - * @param person - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entity - * object that will be created in the S/4HANA system. - * @return A request builder to create a new - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entity. To - * perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.CreateRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - CreateRequestBuilder createPeople( @Nonnull final Person person ); - - /** - * Update an existing {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} - * entity and save it to the S/4HANA system. - * - * @param person - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entity - * object that will be updated in the S/4HANA system. - * @return A request builder to update an existing - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entity. To - * perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.UpdateRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - UpdateRequestBuilder updatePeople( @Nonnull final Person person ); - - /** - * Deletes an existing {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} - * entity in the S/4HANA system. - * - * @param person - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entity - * object that will be deleted in the S/4HANA system. - * @return A request builder to delete an existing - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person Person} entity. To - * perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.DeleteRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - DeleteRequestBuilder deletePeople( @Nonnull final Person person ); - - /** - * Fetch multiple {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} - * entities. - * - * @return A request builder to fetch multiple - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entities. - * This request builder allows methods which modify the underlying query to be called before executing the - * query itself. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - GetAllRequestBuilder getAllAirlines(); - - /** - * Fetch the number of entries from the - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entity collection - * matching the filter and search expressions. - * - * @return A request builder to fetch the count of - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entities. - * This request builder allows methods which modify the underlying query to be called before executing the - * query itself. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.CountRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - CountRequestBuilder countAirlines(); - - /** - * Fetch a single {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} - * entity using key fields. - * - * @param airlineCode - *

- * Constraints: Not nullable - *

- * @return A request builder to fetch a single - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entity - * using key fields. This request builder allows methods which modify the underlying query to be called - * before executing the query itself. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.GetByKeyRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - GetByKeyRequestBuilder getAirlinesByKey( final String airlineCode ); - - /** - * Create a new {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} - * entity and save it to the S/4HANA system. - * - * @param airline - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entity - * object that will be created in the S/4HANA system. - * @return A request builder to create a new - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entity. - * To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.CreateRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - CreateRequestBuilder createAirlines( @Nonnull final Airline airline ); - - /** - * Update an existing {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline - * Airline} entity and save it to the S/4HANA system. - * - * @param airline - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entity - * object that will be updated in the S/4HANA system. - * @return A request builder to update an existing - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entity. - * To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.UpdateRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - UpdateRequestBuilder updateAirlines( @Nonnull final Airline airline ); - - /** - * Deletes an existing {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline - * Airline} entity in the S/4HANA system. - * - * @param airline - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entity - * object that will be deleted in the S/4HANA system. - * @return A request builder to delete an existing - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airline Airline} entity. - * To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.DeleteRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - DeleteRequestBuilder deleteAirlines( @Nonnull final Airline airline ); - - /** - * Fetch multiple {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} - * entities. - * - * @return A request builder to fetch multiple - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entities. - * This request builder allows methods which modify the underlying query to be called before executing the - * query itself. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.GetAllRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - GetAllRequestBuilder getAllAirports(); - - /** - * Fetch the number of entries from the - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entity collection - * matching the filter and search expressions. - * - * @return A request builder to fetch the count of - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entities. - * This request builder allows methods which modify the underlying query to be called before executing the - * query itself. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.CountRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - CountRequestBuilder countAirports(); - - /** - * Fetch a single {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} - * entity using key fields. - * - * @param icaoCode - *

- * Constraints: Not nullable - *

- * @return A request builder to fetch a single - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entity - * using key fields. This request builder allows methods which modify the underlying query to be called - * before executing the query itself. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.GetByKeyRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - GetByKeyRequestBuilder getAirportsByKey( final String icaoCode ); - - /** - * Create a new {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} - * entity and save it to the S/4HANA system. - * - * @param airport - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entity - * object that will be created in the S/4HANA system. - * @return A request builder to create a new - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entity. - * To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.CreateRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - CreateRequestBuilder createAirports( @Nonnull final Airport airport ); - - /** - * Update an existing {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport - * Airport} entity and save it to the S/4HANA system. - * - * @param airport - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entity - * object that will be updated in the S/4HANA system. - * @return A request builder to update an existing - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entity. - * To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.UpdateRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - UpdateRequestBuilder updateAirports( @Nonnull final Airport airport ); - - /** - * Deletes an existing {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport - * Airport} entity in the S/4HANA system. - * - * @param airport - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entity - * object that will be deleted in the S/4HANA system. - * @return A request builder to delete an existing - * {@link com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Airport Airport} entity. - * To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.DeleteRequestBuilder#execute - * execute} method on the request builder object. - */ - @Nonnull - DeleteRequestBuilder deleteAirports( @Nonnull final Airport airport ); - - /** - *

- * Creates a request builder for the GetPersonWithMostFriends OData function. - *

- * - * @return A request builder object that will execute the GetPersonWithMostFriends OData function with the - * provided parameters. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.SingleValueFunctionRequestBuilder#execute execute} method - * on the request builder object. - */ - @Nonnull - SingleValueFunctionRequestBuilder getPersonWithMostFriends(); - - /** - *

- * Creates a request builder for the GetNearestAirport OData function. - *

- * - * @param lon - * Constraints: Not nullable - *

- * Original parameter name from the Odata EDM: lon - *

- * @param lat - * Constraints: Not nullable - *

- * Original parameter name from the Odata EDM: lat - *

- * @return A request builder object that will execute the GetNearestAirport OData function with the provided - * parameters. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.SingleValueFunctionRequestBuilder#execute execute} method - * on the request builder object. - */ - @Nonnull - SingleValueFunctionRequestBuilder - getNearestAirport( @Nonnull final Double lat, @Nonnull final Double lon ); - - /** - *

- * Creates a request builder for the ResetDataSource OData action. - *

- * - * @return A request builder object that will execute the ResetDataSource OData action with the provided - * parameters. To perform execution, call the - * {@link com.sap.cloud.sdk.datamodel.odatav4.core.SingleValueActionRequestBuilder#execute execute} method - * on the request builder object. - */ - @Nonnull - SingleValueActionRequestBuilder resetDataSource(); - -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/CollectionValueActionRequestBuilderTest/ActionNoParametersRequestBody.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/CollectionValueActionRequestBuilderTest/ActionNoParametersRequestBody.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/CollectionValueActionRequestBuilderTest/ActionNoParametersRequestBody.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/CsrfTokenHandlingTest/CreateResponse.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/CsrfTokenHandlingTest/CreateResponse.json deleted file mode 100644 index 6993dec68c..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/CsrfTokenHandlingTest/CreateResponse.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "@odata.context": "/sap/ODATA_SRV$metadata#People/$entity", - "ShoeSize": 46 -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ETagTest/GetAllResponseBody.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ETagTest/GetAllResponseBody.json deleted file mode 100644 index 379421658b..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ETagTest/GetAllResponseBody.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "value": [ - { - "@context": "context", - "@type": "type", - "@etag": "W/\"123\"", - "UserName": "russellwhyte", - "FirstName": "Russell", - "LastName": "Whyte" - }, - { - "@context": "context", - "@type": "type", - "@etag": "W/\"999\"", - "UserName": "scottketchum", - "FirstName": "Scott", - "LastName": "Ketchum" - } - ] -} \ No newline at end of file diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ETagTest/GetSingleResponseBody.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ETagTest/GetSingleResponseBody.json deleted file mode 100644 index 552beb42bf..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ETagTest/GetSingleResponseBody.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "UserName": "russellwhyte", - "FirstName": "Russell", - "LastName": "Whyte" -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/HeadersHandlingTest/CreateResponse.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/HeadersHandlingTest/CreateResponse.json deleted file mode 100644 index 6993dec68c..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/HeadersHandlingTest/CreateResponse.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "@odata.context": "/sap/ODATA_SRV$metadata#People/$entity", - "ShoeSize": 46 -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchEmptyRequest.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchEmptyRequest.txt deleted file mode 100644 index 87e43fd868..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchEmptyRequest.txt +++ /dev/null @@ -1 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchEmptyResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchEmptyResponse.txt deleted file mode 100644 index 906fce312f..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchEmptyResponse.txt +++ /dev/null @@ -1,2 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsErrorResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsErrorResponse.txt deleted file mode 100644 index a4b70ad11a..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsErrorResponse.txt +++ /dev/null @@ -1,21 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People","value":[{"UserName":"angelhuffman","FirstName":"Angel","LastName":"Huffman"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 400 Bad Request -Content-Type: application/json;charset=utf-8 -Content-Length: 1050 -dataserviceversion: 1.0 - -{"error":{"code":"ZCU/100","message":{"lang":"nl","value":"Service 0000000003 0000000010 niet gevonden voor operatie 0410"},"innererror":{"application":{"component_id":"","service_namespace":"/SAP/","service_id":"ZCU_PE_ORDER_SRV","service_version":"0001"},"transactionid":"23F2932D54040110E005FD84A23B406E","timestamp":"20201215151501.0634490","Error_Resolution":{"SAP_Transaction":"Run transaction /IWFND/ERROR_LOG on SAP Gateway hub system (System Alias ) and search for entries with the timestamp above for more details","SAP_Note":"See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)","Batch_SAP_Note":"See SAP Note 1869434 for details about working with $batch (https://service.sap.com/sap/support/notes/1869434)"},"errordetails":[{"code":"ZCU/100","message":"Service 0000000003 0000000010 niet gevonden voor operatie 0410","propertyref":"","severity":"error","target":""},{"code":"/IWBEP/CX_MGW_BUSI_EXCEPTION","message":"Fout bij wijzigen PE order.","propertyref":"","severity":"error","target":""}]}}} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsMissingRequest.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsMissingRequest.txt deleted file mode 100644 index 65acbfb138..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsMissingRequest.txt +++ /dev/null @@ -1,28 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -GET People(%27one%27) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -GET People(%27two%27) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -GET People(%27three%27) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsMissingResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsMissingResponse.txt deleted file mode 100644 index 14db6511d8..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsMissingResponse.txt +++ /dev/null @@ -1,20 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People","value":[{"UserName":"one"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 404 Not Found -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"error":{"code":"","message":"The request resource is not found."}} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsRequest.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsRequest.txt deleted file mode 100644 index 075656d033..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsRequest.txt +++ /dev/null @@ -1,19 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -GET People?$top=1 HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -GET People?$top=2&$skip=1 HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsResponse.txt deleted file mode 100644 index 04c690beb0..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchOnlyReadsResponse.txt +++ /dev/null @@ -1,21 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People","value":[{"UserName":"angelhuffman","FirstName":"Angel","LastName":"Huffman"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People","value":[{"UserName":"klauskinski","FirstName":"Klaus","LastName":"Kinski"},{"UserName":"DanielBruehl","FirstName":"Daniel","LastName":"Brühl"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesErrorRequest.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesErrorRequest.txt deleted file mode 100644 index 929ca524f6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesErrorRequest.txt +++ /dev/null @@ -1,37 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000002 - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -POST People HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"UserName":"JohnDoe1", "FirstName":"John"} - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -POST People HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"UserName":"JohnDoe2"} - ---changeset_00000000-0000-0000-0000-000000000002-- - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -GET People(%27klauskinski%27) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesErrorResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesErrorResponse.txt deleted file mode 100644 index 7486fe8290..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesErrorResponse.txt +++ /dev/null @@ -1,23 +0,0 @@ ---batchresponse_2e5e59e3-ef58-493c-b947-638f445896aa -Content-Type: multipart/mixed; boundary=changesetresponse_4459f831-1259-4f7c-89a0-95162d172489 - ---changesetresponse_4459f831-1259-4f7c-89a0-95162d172489 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -HTTP/1.1 400 Bad Request -Content-Type: application/json; charset=utf-8 - -{"error": {"code": "005056A509B11EE1B9A8FEC11C23378E","message": {"lang": "en","value": "The FirstName field is required"}}} ---changesetresponse_4459f831-1259-4f7c-89a0-95162d172489-- ---batchresponse_2e5e59e3-ef58-493c-b947-638f445896aa -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People/$entity","UserName":"klauskinski","FirstName":"Klaus","LastName":"Kinski","MiddleName":null,"Gender":"Male","Age":null,"Emails":[],"FavoriteFeature":null,"Features":[],"AddressInfo":[],"HomeAddress":null} ---batchresponse_2e5e59e3-ef58-493c-b947-638f445896aa-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesErrorResponseWithoutChangeset.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesErrorResponseWithoutChangeset.txt deleted file mode 100644 index 95a0c1abde..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesErrorResponseWithoutChangeset.txt +++ /dev/null @@ -1,19 +0,0 @@ ---batchresponse_2e5e59e3-ef58-493c-b947-638f445896aa -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -HTTP/1.1 400 Bad Request -Content-Type: application/json; charset=utf-8 - -[{"Id":"System.ComponentModel.DataAnnotations.RequiredAttribute","Message":"The FirstName field is required.","PropertyName":"FirstName","Severity":"Error"}] ---batchresponse_2e5e59e3-ef58-493c-b947-638f445896aa -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People/$entity","UserName":"klauskinski","FirstName":"Klaus","LastName":"Kinski","MiddleName":null,"Gender":"Male","Age":null,"Emails":[],"FavoriteFeature":null,"Features":[],"AddressInfo":[],"HomeAddress":null} ---batchresponse_2e5e59e3-ef58-493c-b947-638f445896aa-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesSuccessRequest.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesSuccessRequest.txt deleted file mode 100644 index c1517843ad..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesSuccessRequest.txt +++ /dev/null @@ -1,37 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000002 - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -POST People HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"UserName":"JohnDoe1", "FirstName":"John"} - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -POST People HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"UserName":"JohnDoe2", "FirstName":"John"} - ---changeset_00000000-0000-0000-0000-000000000002-- - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -GET People(%27foo%27) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesSuccessResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesSuccessResponse.txt deleted file mode 100644 index c69262bcd8..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataClientBatchResponseParsingUnitTest/BatchReadsAndWritesSuccessResponse.txt +++ /dev/null @@ -1,37 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -HTTP/1.1 201 Created -Location: https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/People('menow2') -Content-Type: application/json; odata.metadata=minimal -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People/$entity","UserName":"JohnDoe1","FirstName":"John","LastName":"","MiddleName":null,"Gender":"Male","Age":null,"Emails":[],"FavoriteFeature":null,"Features":[],"AddressInfo":[],"HomeAddress":null} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -HTTP/1.1 201 Created -Location: https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/People('menow3') -Content-Type: application/json; odata.metadata=minimal -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People/$entity","UserName":"JohnDoe2","FirstName":"John","LastName":"","MiddleName":null,"Gender":"Male","Age":null,"Emails":[],"FavoriteFeature":null,"Features":[],"AddressInfo":[],"HomeAddress":null} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4-- ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 404 Not Found -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"error":{"code":"","message":"The request resource is not found."}} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchEmptyRequest.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchEmptyRequest.txt deleted file mode 100644 index 87e43fd868..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchEmptyRequest.txt +++ /dev/null @@ -1 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchEmptyResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchEmptyResponse.txt deleted file mode 100644 index 906fce312f..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchEmptyResponse.txt +++ /dev/null @@ -1,2 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchOnlyReadsErrorResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchOnlyReadsErrorResponse.txt deleted file mode 100644 index 338622ee98..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchOnlyReadsErrorResponse.txt +++ /dev/null @@ -1,20 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People","value":[{"UserName":"angelhuffman","FirstName":"Angel","LastName":"Huffman"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 400 Bad Request -Content-Type: application/json;charset=utf-8 -Content-Length: 1050 - -{"error":{"code":"ZCU/100","message":{"lang":"nl","value":"Service 0000000003 0000000010 niet gevonden voor operatie 0410"},"innererror":{"application":{"component_id":"","service_namespace":"/SAP/","service_id":"ZCU_PE_ORDER_SRV","service_version":"0001"},"transactionid":"23F2932D54040110E005FD84A23B406E","timestamp":"20201215151501.0634490","Error_Resolution":{"SAP_Transaction":"Run transaction /IWFND/ERROR_LOG on SAP Gateway hub system (System Alias ) and search for entries with the timestamp above for more details","SAP_Note":"See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)","Batch_SAP_Note":"See SAP Note 1869434 for details about working with $batch (https://service.sap.com/sap/support/notes/1869434)"},"errordetails":[{"code":"ZCU/100","message":"Service 0000000003 0000000010 niet gevonden voor operatie 0410","propertyref":"","severity":"error","target":""},{"code":"/IWBEP/CX_MGW_BUSI_EXCEPTION","message":"Fout bij wijzigen PE order.","propertyref":"","severity":"error","target":""}]}}} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchOnlyReadsRequest.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchOnlyReadsRequest.txt deleted file mode 100644 index 075656d033..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchOnlyReadsRequest.txt +++ /dev/null @@ -1,19 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -GET People?$top=1 HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -GET People?$top=2&$skip=1 HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchOnlyReadsResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchOnlyReadsResponse.txt deleted file mode 100644 index 04c690beb0..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchOnlyReadsResponse.txt +++ /dev/null @@ -1,21 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People","value":[{"UserName":"angelhuffman","FirstName":"Angel","LastName":"Huffman"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People","value":[{"UserName":"klauskinski","FirstName":"Klaus","LastName":"Kinski"},{"UserName":"DanielBruehl","FirstName":"Daniel","LastName":"Brühl"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchReadsAndWritesSuccessRequest.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchReadsAndWritesSuccessRequest.txt deleted file mode 100644 index b568462c2e..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchReadsAndWritesSuccessRequest.txt +++ /dev/null @@ -1,37 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000002 - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -POST People HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"@odata.type":"#Trippin.Person","UserName":"JohnDoe1","FirstName":"John","Friends":[],"Trips":[]} - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -POST People HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"@odata.type":"#Trippin.Person","UserName":"JohnDoe2","FirstName":"John","Friends":[],"Trips":[]} - ---changeset_00000000-0000-0000-0000-000000000002-- - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -GET People(%27foo%27) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchReadsAndWritesSuccessResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchReadsAndWritesSuccessResponse.txt deleted file mode 100644 index c69262bcd8..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchReferenceServiceUnitTest/BatchReadsAndWritesSuccessResponse.txt +++ /dev/null @@ -1,37 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -HTTP/1.1 201 Created -Location: https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/People('menow2') -Content-Type: application/json; odata.metadata=minimal -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People/$entity","UserName":"JohnDoe1","FirstName":"John","LastName":"","MiddleName":null,"Gender":"Male","Age":null,"Emails":[],"FavoriteFeature":null,"Features":[],"AddressInfo":[],"HomeAddress":null} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -HTTP/1.1 201 Created -Location: https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/People('menow3') -Content-Type: application/json; odata.metadata=minimal -OData-Version: 4.0 - -{"@odata.context":"https://services.odata.org/TripPinRESTierService/(S(w3zgpoiit3rigb4hkixmletd))/$metadata#People/$entity","UserName":"JohnDoe2","FirstName":"John","LastName":"","MiddleName":null,"Gender":"Male","Age":null,"Emails":[],"FavoriteFeature":null,"Features":[],"AddressInfo":[],"HomeAddress":null} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4-- ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 404 Not Found -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"error":{"code":"","message":"The request resource is not found."}} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- - diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchRequestUnitTest/BatchReadsAndWritesSuccessRequest.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchRequestUnitTest/BatchReadsAndWritesSuccessRequest.txt deleted file mode 100644 index 9f0b46136d..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchRequestUnitTest/BatchReadsAndWritesSuccessRequest.txt +++ /dev/null @@ -1,100 +0,0 @@ ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -GET EntityCollection HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -GET EntityCollection(%27foobar%27) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000002 - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -POST EntityCollection HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"@odata.type":"#TestEntity"} - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 4 - -PATCH EntityCollection(%27upd%27) HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"@odata.type":"#TestEntity"} - ---changeset_00000000-0000-0000-0000-000000000002 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 5 - -DELETE EntityCollection(%27del%27) HTTP/1.1 -Accept: application/json - - ---changeset_00000000-0000-0000-0000-000000000002-- - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 6 - -GET function-single(secret=%27pass%27) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 7 - -GET function-multiple(secret=%27pass%27) HTTP/1.1 -Accept: application/json - - ---batch_00000000-0000-0000-0000-000000000001 -Content-Type: multipart/mixed;boundary=changeset_00000000-0000-0000-0000-000000000003 - ---changeset_00000000-0000-0000-0000-000000000003 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 8 - -POST action-single HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"secret":"pass"} - ---changeset_00000000-0000-0000-0000-000000000003 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 9 - -POST action-multiple HTTP/1.1 -Accept: application/json -Content-Type: application/json - -{"secret":"pass"} - ---changeset_00000000-0000-0000-0000-000000000003-- - ---batch_00000000-0000-0000-0000-000000000001-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchRequestUnitTest/BatchReadsAndWritesSuccessResponse.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchRequestUnitTest/BatchReadsAndWritesSuccessResponse.txt deleted file mode 100644 index 7290820ed7..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODataV4BatchRequestUnitTest/BatchReadsAndWritesSuccessResponse.txt +++ /dev/null @@ -1,87 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity","value":[{"id":"adam"},{"id":"eve"},{"id":"foobar"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity/$entity","id":"foobar"} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -HTTP/1.1 201 Created -Location: https://localhost/service/TestEntities('new') -Content-Type: application/json; odata.metadata=minimal -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity","id":"new"} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity","id":"updated"} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -HTTP/1.1 204 No Content - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4-- ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: text/plain - -42 ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"value":["Something","here"]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"value":42} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000 -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json - -{"value":["Something","here"]} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8000-- ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODatav4BatchConnectionTest/BatchResponseWithChangeset.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODatav4BatchConnectionTest/BatchResponseWithChangeset.txt deleted file mode 100644 index bc821ac212..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODatav4BatchConnectionTest/BatchResponseWithChangeset.txt +++ /dev/null @@ -1,51 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity","value":[{"id":"adam"},{"id":"eve"},{"id":"foobar"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity/$entity","id":"foobar"} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: multipart/mixed; boundary=changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 1 - -HTTP/1.1 201 Created -Location: https://localhost/service/TestEntities('new') -Content-Type: application/json; odata.metadata=minimal -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity","id":"new"} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 2 - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity","id":"updated"} ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4 -Content-Type: application/http -Content-Transfer-Encoding: binary -Content-ID: 3 - -HTTP/1.1 204 No Content - ---changesetresponse_e4c6cc48-c59e-42f8-bb00-1250fa2a8cb4-- ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODatav4BatchConnectionTest/BatchResponseWithError.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODatav4BatchConnectionTest/BatchResponseWithError.txt deleted file mode 100644 index f2d88779c4..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODatav4BatchConnectionTest/BatchResponseWithError.txt +++ /dev/null @@ -1,28 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity","value":[{"id":"adam"},{"id":"eve"},{"id":"foobar"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity/$entity","id":"foobar"} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 400 Bad Request -Content-Type: application/json;charset=utf-8 -Content-Length: 1050 - -{"error":{"code":"ZCU/100","message":{"lang":"nl","value":"Service 0000000003 0000000010 niet gevonden voor operatie 0410"},"innererror":{"application":{"component_id":"","service_namespace":"/SAP/","service_id":"ZCU_PE_ORDER_SRV","service_version":"0001"},"transactionid":"23F2932D54040110E005FD84A23B406E","timestamp":"20201215151501.0634490","Error_Resolution":{"SAP_Transaction":"Run transaction /IWFND/ERROR_LOG on SAP Gateway hub system (System Alias ) and search for entries with the timestamp above for more details","SAP_Note":"See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)","Batch_SAP_Note":"See SAP Note 1869434 for details about working with $batch (https://service.sap.com/sap/support/notes/1869434)"},"errordetails":[{"code":"ZCU/100","message":"Service 0000000003 0000000010 niet gevonden voor operatie 0410","propertyref":"","severity":"error","target":""},{"code":"/IWBEP/CX_MGW_BUSI_EXCEPTION","message":"Fout bij wijzigen PE order.","propertyref":"","severity":"error","target":""}]}}} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- \ No newline at end of file diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODatav4BatchConnectionTest/BatchResponseWithoutChangeset.txt b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODatav4BatchConnectionTest/BatchResponseWithoutChangeset.txt deleted file mode 100644 index 359e8efa0e..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/ODatav4BatchConnectionTest/BatchResponseWithoutChangeset.txt +++ /dev/null @@ -1,19 +0,0 @@ ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity","value":[{"id":"adam"},{"id":"eve"},{"id":"foobar"}]} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef -Content-Type: application/http -Content-Transfer-Encoding: binary - -HTTP/1.1 200 OK -Content-Type: application/json; odata.metadata=minimal; odata.streaming=true -OData-Version: 4.0 - -{"@odata.context":"https://localhost/service/$metadata#TestEntity/$entity","id":"foobar"} ---batchresponse_76ef6b0a-a0e2-4f31-9f70-f5d3f73a6bef-- \ No newline at end of file diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionComplexTypeRequestBody.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionComplexTypeRequestBody.json deleted file mode 100644 index 58613d4755..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionComplexTypeRequestBody.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "complexEntity": {"@odata.type": "#com.sap.cloud.sdk.ComplexType", - "City": "Stockholm", - "Country": "Sweden" - } -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionEntityRequestBody.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionEntityRequestBody.json deleted file mode 100644 index c2ec3886d8..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionEntityRequestBody.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "entityParameter": {"@odata.type": "#com.sap.cloud.sdk.TestEntity", - "Name": "Tester"} -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionNoParametersRequestBody.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionNoParametersRequestBody.json deleted file mode 100644 index 9e26dfeeb6..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionNoParametersRequestBody.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionNullEntityRequestBody.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionNullEntityRequestBody.json deleted file mode 100644 index 1714b4861f..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionNullEntityRequestBody.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "entityParameter": null -} diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionParametersRequestBody.json b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionParametersRequestBody.json deleted file mode 100644 index e0c51b0612..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/SingleValueActionRequestBuilderTest/ActionParametersRequestBody.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "stringParameter" : "test", - "booleanParameter" : true, - "integerParameter" : 9000, - "decimalParameter" : 3.14, - "durationParameter" : "PT8H", - "dateTimeOffsetParameter":"2020-03-12T05:02:30Z", - "timeOfDayParameter":"13:03:39.999" -} \ No newline at end of file diff --git a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/unused_trippin.edmx b/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/unused_trippin.edmx deleted file mode 100644 index 26ed9a8945..0000000000 --- a/datamodel/odata-v4-core-apache-httpclient5/src/test/resources/unused_trippin.edmx +++ /dev/null @@ -1,219 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Name - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/datamodel/pom.xml b/datamodel/pom.xml index 9018e060aa..fb51f9f405 100644 --- a/datamodel/pom.xml +++ b/datamodel/pom.xml @@ -35,9 +35,7 @@ odata-client odata-client-apache-httpclient5 odata - odata-core-apache-httpclient5 odata-v4 - odata-v4-core-apache-httpclient5 soap openapi diff --git a/release_notes.md b/release_notes.md index ed6620e985..3112514a9d 100644 --- a/release_notes.md +++ b/release_notes.md @@ -12,7 +12,7 @@ ### ✨ New Functionality -- Introduced the `odata-core-apache-httpclient5` and `odata-v4-core-apache-httpclient5` modules, which run on top of Apache HttpClient 5. These are drop-in replacements for `odata-core` and `odata-v4-core` (same Java packages) for consumers moving off the end-of-life Apache HttpClient 4.x stack. +- ### 📈 Improvements