From 3739971ad3c0ef48ee89fa9c4feea3bd5c688295 Mon Sep 17 00:00:00 2001 From: Eric Daniels Date: Thu, 26 Jul 2018 10:04:45 -0400 Subject: [PATCH] STITCH-1817 - Expose StitchServiceClient and add AwsServiceClient --- .../stitch/android/core/StitchAppClient.java | 10 +- .../core/internal/StitchAppClientImpl.java | 46 ++-- .../{internal => }/StitchServiceClient.java | 133 ++++++++-- .../internal/NamedServiceClientFactory.java | 3 +- .../internal/ServiceClientFactory.java | 3 +- .../internal/StitchServiceClientImpl.java | 105 ++++++-- .../android/core/services/package-info.java | 20 ++ .../services/aws/s3/AwsS3ServiceClient.java | 7 +- .../s3/internal/AwsS3ServiceClientImpl.java | 4 + .../services/aws/ses/AwsSesServiceClient.java | 7 +- .../ses/internal/AwsSesServiceClientImpl.java | 4 + android/services/aws/build.gradle | 46 ++++ .../services/aws/AwsServiceClientIntTests.kt | 132 ++++++++++ .../services/aws/src/main/AndroidManifest.xml | 2 + .../services/aws/AwsServiceClient.java | 190 ++++++++++++++ .../aws/internal/AwsServiceClientImpl.java | 246 ++++++++++++++++++ .../services/aws/internal/package-info.java | 20 ++ .../android/services/aws/package-info.java | 18 ++ .../aws/src/main/res/values/strings.xml | 2 + .../services/fcm/FcmServiceClient.java | 5 +- .../http/HttpServiceClientIntTests.kt | 2 +- .../services/http/HttpServiceClient.java | 4 +- .../mongodb/local/LocalMongoDbService.java | 4 +- .../mongodb/remote/RemoteMongoClient.java | 4 +- .../services/twilio/TwilioServiceClient.java | 4 +- .../core/admin/services/ServiceConfigs.kt | 1 + .../admin/services/rules/RulesResources.kt | 1 + .../core/internal/CoreStitchAppClient.java | 16 +- .../internal/CoreStitchServiceClient.java | 25 +- .../internal/CoreStitchServiceClientImpl.java | 31 ++- .../internal/CoreStitchServiceUnitTests.java | 6 +- .../services/aws/s3/AwsS3PutObjectResult.java | 3 + .../aws/s3/AwsS3SignPolicyResult.java | 3 + .../s3/internal/CoreAwsS3ServiceClient.java | 8 +- .../aws/s3/internal/ResultDecoders.java | 4 + .../CoreAwsS3ServiceClientUnitTests.java | 18 +- .../services/aws/ses/AwsSesSendResult.java | 3 + .../ses/internal/CoreAwsSesServiceClient.java | 6 +- .../aws/ses/internal/ResultDecoders.java | 4 + .../CoreAwsSesServiceClientUnitTests.java | 6 +- core/services/aws/build.gradle | 22 ++ .../stitch/core/services/aws/AwsRequest.java | 157 +++++++++++ .../aws/internal/CoreAwsServiceClient.java | 237 +++++++++++++++++ .../services/aws/internal/package-info.java | 20 ++ .../core/services/aws/package-info.java | 21 ++ .../services/aws/AwsRequestUnitTests.java | 62 +++++ .../CoreAwsServiceClientUnitTests.java | 119 +++++++++ .../fcm/internal/CoreFcmServiceClient.java | 6 +- .../CoreFcmServiceClientUnitTests.java | 10 +- .../http/internal/CoreHttpServiceClient.java | 2 +- .../CoreHttpServiceClientUnitTests.java | 6 +- .../remote/internal/AggregateOperation.java | 2 +- .../remote/internal/CountOperation.java | 2 +- .../remote/internal/DeleteManyOperation.java | 2 +- .../remote/internal/DeleteOneOperation.java | 2 +- .../remote/internal/FindOperation.java | 2 +- .../remote/internal/InsertManyOperation.java | 2 +- .../remote/internal/InsertOneOperation.java | 2 +- .../remote/internal/UpdateManyOperation.java | 2 +- .../remote/internal/UpdateOneOperation.java | 2 +- .../CoreRemoteMongoCollectionUnitTests.java | 66 ++--- .../internal/CoreTwilioServiceClient.java | 2 +- .../CoreTwilioServiceClientUnitTests.java | 8 +- .../stitch/server/core/StitchAppClient.java | 10 +- .../core/internal/StitchAppClientImpl.java | 41 +-- .../{internal => }/StitchServiceClient.java | 128 +++++++-- .../internal/NamedServiceClientFactory.java | 5 +- .../internal/ServiceClientFactory.java | 5 +- .../internal/StitchServiceClientImpl.java | 78 ++++-- .../server/core/services/package-info.java | 20 ++ .../server/core/StitchAppClientIntTests.kt | 5 +- .../services/aws/s3/AwsS3ServiceClient.java | 3 + .../s3/internal/AwsS3ServiceClientImpl.java | 4 + .../services/aws/ses/AwsSesServiceClient.java | 3 + .../ses/internal/AwsSesServiceClientImpl.java | 4 + server/services/aws/build.gradle | 34 +++ .../server/services/aws/AwsServiceClient.java | 175 +++++++++++++ .../aws/internal/AwsServiceClientImpl.java | 189 ++++++++++++++ .../services/aws/internal/package-info.java | 20 ++ .../server/services/aws/package-info.java | 18 ++ .../services/aws/AwsServiceClientIntTests.kt | 127 +++++++++ .../http/HttpServiceClientIntTests.kt | 2 +- .../stitch/server/testutils/TestUtils.kt | 8 - settings.gradle | 6 + 84 files changed, 2554 insertions(+), 243 deletions(-) rename android/core/src/main/java/com/mongodb/stitch/android/core/services/{internal => }/StitchServiceClient.java (52%) create mode 100644 android/core/src/main/java/com/mongodb/stitch/android/core/services/package-info.java create mode 100644 android/services/aws/build.gradle create mode 100644 android/services/aws/src/androidTest/java/com/mongodb/stitch/android/services/aws/AwsServiceClientIntTests.kt create mode 100644 android/services/aws/src/main/AndroidManifest.xml create mode 100644 android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/AwsServiceClient.java create mode 100644 android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/internal/AwsServiceClientImpl.java create mode 100644 android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/internal/package-info.java create mode 100644 android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/package-info.java create mode 100644 android/services/aws/src/main/res/values/strings.xml create mode 100644 core/services/aws/build.gradle create mode 100644 core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/AwsRequest.java create mode 100644 core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/internal/CoreAwsServiceClient.java create mode 100644 core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/internal/package-info.java create mode 100644 core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/package-info.java create mode 100644 core/services/aws/src/test/java/com/mongodb/stitch/core/services/aws/AwsRequestUnitTests.java create mode 100644 core/services/aws/src/test/java/com/mongodb/stitch/core/services/aws/internal/CoreAwsServiceClientUnitTests.java rename server/core/src/main/java/com/mongodb/stitch/server/core/services/{internal => }/StitchServiceClient.java (52%) create mode 100644 server/core/src/main/java/com/mongodb/stitch/server/core/services/package-info.java create mode 100644 server/services/aws/build.gradle create mode 100644 server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/AwsServiceClient.java create mode 100644 server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/internal/AwsServiceClientImpl.java create mode 100644 server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/internal/package-info.java create mode 100644 server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/package-info.java create mode 100644 server/services/aws/src/test/java/com/mongodb/stitch/server/services/aws/AwsServiceClientIntTests.kt delete mode 100644 server/testutils/src/main/java/com/mongodb/stitch/server/testutils/TestUtils.kt diff --git a/android/core/src/main/java/com/mongodb/stitch/android/core/StitchAppClient.java b/android/core/src/main/java/com/mongodb/stitch/android/core/StitchAppClient.java index e10c31863..ae48b3209 100644 --- a/android/core/src/main/java/com/mongodb/stitch/android/core/StitchAppClient.java +++ b/android/core/src/main/java/com/mongodb/stitch/android/core/StitchAppClient.java @@ -19,9 +19,9 @@ import com.google.android.gms.tasks.Task; import com.mongodb.stitch.android.core.auth.StitchAuth; import com.mongodb.stitch.android.core.push.StitchPush; +import com.mongodb.stitch.android.core.services.StitchServiceClient; import com.mongodb.stitch.android.core.services.internal.NamedServiceClientFactory; import com.mongodb.stitch.android.core.services.internal.ServiceClientFactory; - import java.io.Closeable; import java.io.IOException; import java.util.List; @@ -71,6 +71,14 @@ public interface StitchAppClient extends Closeable { */ T getServiceClient(final ServiceClientFactory factory); + /** + * Gets a general purpose client for the given named service. + * + * @param serviceName the name of the service. + * @return a client to interact with the service. + */ + StitchServiceClient getServiceClient(final String serviceName); + /** * Calls the specified Stitch function. * diff --git a/android/core/src/main/java/com/mongodb/stitch/android/core/internal/StitchAppClientImpl.java b/android/core/src/main/java/com/mongodb/stitch/android/core/internal/StitchAppClientImpl.java index 64d7dfa34..3aa3e033e 100644 --- a/android/core/src/main/java/com/mongodb/stitch/android/core/internal/StitchAppClientImpl.java +++ b/android/core/src/main/java/com/mongodb/stitch/android/core/internal/StitchAppClientImpl.java @@ -23,6 +23,7 @@ import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.android.core.push.StitchPush; import com.mongodb.stitch.android.core.push.internal.StitchPushImpl; +import com.mongodb.stitch.android.core.services.StitchServiceClient; import com.mongodb.stitch.android.core.services.internal.NamedServiceClientFactory; import com.mongodb.stitch.android.core.services.internal.ServiceClientFactory; import com.mongodb.stitch.android.core.services.internal.StitchServiceClientImpl; @@ -31,7 +32,7 @@ import com.mongodb.stitch.core.internal.CoreStitchAppClient; import com.mongodb.stitch.core.internal.net.StitchAppRoutes; import com.mongodb.stitch.core.internal.net.StitchRequestClient; - +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClientImpl; import java.io.IOException; import java.util.List; import java.util.concurrent.Callable; @@ -90,12 +91,11 @@ public StitchPush getPush() { public T getServiceClient( final NamedServiceClientFactory factory, final String serviceName) { return factory.getClient( - new StitchServiceClientImpl( + new CoreStitchServiceClientImpl( auth, routes.getServiceRoutes(), serviceName, - info.getCodecRegistry(), - dispatcher), + info.getCodecRegistry()), info, dispatcher); } @@ -103,23 +103,33 @@ public T getServiceClient( @Override public T getServiceClient(final ServiceClientFactory factory) { return factory.getClient( - new StitchServiceClientImpl( - auth, - routes.getServiceRoutes(), - "", - info.getCodecRegistry(), - dispatcher), + new CoreStitchServiceClientImpl( + auth, + routes.getServiceRoutes(), + "", + info.getCodecRegistry()), info, dispatcher); } + @Override + public StitchServiceClient getServiceClient(final String serviceName) { + return new StitchServiceClientImpl( + new CoreStitchServiceClientImpl( + auth, + routes.getServiceRoutes(), + serviceName, + info.getCodecRegistry()), + dispatcher); + } + @Override public Task callFunction(final String name, final List args) { return dispatcher.dispatchTask( new Callable() { @Override public Void call() { - coreClient.callFunctionInternal(name, args, null); + coreClient.callFunction(name, args, null); return null; } }); @@ -135,7 +145,7 @@ public Task callFunction( new Callable() { @Override public Void call() { - coreClient.callFunctionInternal(name, args, requestTimeout); + coreClient.callFunction(name, args, requestTimeout); return null; } }); @@ -148,7 +158,7 @@ public Task callFunction( new Callable() { @Override public ResultT call() { - return coreClient.callFunctionInternal(name, args, null, resultClass); + return coreClient.callFunction(name, args, null, resultClass); } }); } @@ -163,7 +173,7 @@ public Task callFunction( new Callable() { @Override public ResultT call() { - return coreClient.callFunctionInternal(name, args, requestTimeout, resultClass); + return coreClient.callFunction(name, args, requestTimeout, resultClass); } }); } @@ -179,7 +189,7 @@ public Task callFunction( new Callable() { @Override public ResultT call() { - return coreClient.callFunctionInternal(name, args, null, resultClass, codecRegistry); + return coreClient.callFunction(name, args, null, resultClass, codecRegistry); } }); } @@ -196,7 +206,7 @@ public Task callFunction( new Callable() { @Override public ResultT call() { - return coreClient.callFunctionInternal( + return coreClient.callFunction( name, args, requestTimeout, @@ -213,7 +223,7 @@ public Task callFunction( new Callable() { @Override public ResultT call() { - return coreClient.callFunctionInternal(name, args, null, resultDecoder); + return coreClient.callFunction(name, args, null, resultDecoder); } }); } @@ -228,7 +238,7 @@ public Task callFunction( new Callable() { @Override public ResultT call() { - return coreClient.callFunctionInternal(name, args, requestTimeout, resultDecoder); + return coreClient.callFunction(name, args, requestTimeout, resultDecoder); } }); } diff --git a/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/StitchServiceClient.java b/android/core/src/main/java/com/mongodb/stitch/android/core/services/StitchServiceClient.java similarity index 52% rename from android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/StitchServiceClient.java rename to android/core/src/main/java/com/mongodb/stitch/android/core/services/StitchServiceClient.java index 6e3531380..2a5e9694e 100644 --- a/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/StitchServiceClient.java +++ b/android/core/src/main/java/com/mongodb/stitch/android/core/services/StitchServiceClient.java @@ -14,15 +14,43 @@ * limitations under the License. */ -package com.mongodb.stitch.android.core.services.internal; +package com.mongodb.stitch.android.core.services; import com.google.android.gms.tasks.Task; -import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; - import java.util.List; import org.bson.codecs.Decoder; +import org.bson.codecs.configuration.CodecRegistry; + +/** + * StitchServiceClient acts as a general purpose client for working with services that either + * not defined or well defined by this SDK. It functions similarly to + * {@link com.mongodb.stitch.android.core.StitchAppClient#callFunction}. + */ +public interface StitchServiceClient { + + /** + * Calls the specified Stitch service function. + * + * @param name the name of the Stitch service function to call. + * @param args the arguments to pass to the function. + * @return a {@link Task} that completes when the request completes. + */ + Task callFunction( + final String name, final List args); + + /** + * Calls the specified Stitch service function, and decodes the response into a value using the + * provided {@link Decoder}. + * + * @param name the name of the Stitch service function to call. + * @param args the arguments to pass to the function. + * @param resultDecoder the {@link Decoder} to use to decode the response into a value. + * @param the type into which the response will be decoded. + * @return a {@link Task} containing the decoded value. + */ + Task callFunction( + final String name, final List args, final Decoder resultDecoder); -public interface StitchServiceClient extends CoreStitchServiceClient { /** * Calls the specified Stitch service function, and decodes the response into an instance of the * specified type. The response will be decoded using the codec registry specified when the app @@ -41,50 +69,71 @@ Task callFunction( /** * Calls the specified Stitch service function, and decodes the response into an instance of the - * specified type. The response will be decoded using the codec registry specified when the app - * client was configured. If no codec registry was configured, a default codec registry will be - * used. The default codec registry supports the mappings specified here - * Also accepts a timeout in milliseconds. Use this for functions that may run longer than the - * client-wide default timeout (15 seconds by default). + * specified type. The response will be decoded using the codec registry given. * * @param name the name of the Stitch service function to call. * @param args the arguments to pass to the function. - * @param requestTimeout the number of milliseconds the client should wait for a response from the - * server before failing with an error. * @param resultClass the class that the response should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. * @param the type into which the response will be decoded. * @return a {@link Task} containing the decoded value. */ Task callFunction( - final String name, - final List args, - final Long requestTimeout, - final Class resultClass); + final String name, + final List args, + final Class resultClass, + final CodecRegistry codecRegistry); + + /** + * Calls the specified Stitch service function. + * Also accepts a timeout in milliseconds. Use this for functions that may run longer than the + * client-wide default timeout (15 seconds by default). + * + * @param name the name of the Stitch service function to call. + * @param args the arguments to pass to the function. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @return a {@link Task} that completes when the request completes. + */ + Task callFunction( + final String name, + final List args, + final Long requestTimeout); + /** * Calls the specified Stitch service function, and decodes the response into a value using the - * provided {@link Decoder}. + * provided {@link Decoder}. Also accepts a timeout in milliseconds. Use this for functions that + * may run longer than the client-wide default timeout (15 seconds by default). * * @param name the name of the Stitch service function to call. * @param args the arguments to pass to the function. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. * @param resultDecoder the {@link Decoder} to use to decode the response into a value. * @param the type into which the response will be decoded. * @return a {@link Task} containing the decoded value. */ Task callFunction( - final String name, final List args, final Decoder resultDecoder); + final String name, + final List args, + final Long requestTimeout, + final Decoder resultDecoder); /** - * Calls the specified Stitch service function, and decodes the response into a value using the - * provided {@link Decoder}. Also accepts a timeout in milliseconds. Use this for functions that - * may run longer than the client-wide default timeout (15 seconds by default). + * Calls the specified Stitch service function, and decodes the response into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * Also accepts a timeout in milliseconds. Use this for functions that may run longer than the + * client-wide default timeout (15 seconds by default). * * @param name the name of the Stitch service function to call. * @param args the arguments to pass to the function. * @param requestTimeout the number of milliseconds the client should wait for a response from the * server before failing with an error. - * @param resultDecoder the {@link Decoder} to use to decode the response into a value. + * @param resultClass the class that the response should be decoded as. * @param the type into which the response will be decoded. * @return a {@link Task} containing the decoded value. */ @@ -92,5 +141,43 @@ Task callFunction( final String name, final List args, final Long requestTimeout, - final Decoder resultDecoder); + final Class resultClass); + + /** + * Calls the specified Stitch service function, and decodes the response into an instance of the + * specified type. The response will be decoded using the codec registry given. + * Also accepts a timeout in milliseconds. Use this for functions that may run longer than the + * client-wide default timeout (15 seconds by default). + * + * @param name the name of the Stitch service function to call. + * @param args the arguments to pass to the function. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the response should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return a {@link Task} containing the decoded value. + */ + Task callFunction( + final String name, + final List args, + final Long requestTimeout, + final Class resultClass, + final CodecRegistry codecRegistry); + + /** + * Get the codec registry that will be used to decode responses when a codec registry. + * + * @return the {@link CodecRegistry} + */ + CodecRegistry getCodecRegistry(); + + + /** + * Create a new StitchServiceClient instance with a different codec registry. + * + * @param codecRegistry the new {@link CodecRegistry} for the service client. + * @return a new StitchServiceClient instance with the different codec registry + */ + StitchServiceClient withCodecRegistry(final CodecRegistry codecRegistry); } diff --git a/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/NamedServiceClientFactory.java b/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/NamedServiceClientFactory.java index 02e92bb69..288526911 100644 --- a/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/NamedServiceClientFactory.java +++ b/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/NamedServiceClientFactory.java @@ -18,10 +18,11 @@ import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.core.StitchAppClientInfo; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; public interface NamedServiceClientFactory { T getClient( - final StitchServiceClient serviceClient, + final CoreStitchServiceClient serviceClient, final StitchAppClientInfo appInfo, final TaskDispatcher dispatcher); } diff --git a/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/ServiceClientFactory.java b/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/ServiceClientFactory.java index fe9d58ff2..706d2c4fd 100644 --- a/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/ServiceClientFactory.java +++ b/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/ServiceClientFactory.java @@ -18,10 +18,11 @@ import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.core.StitchAppClientInfo; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; public interface ServiceClientFactory { T getClient( - final StitchServiceClient serviceClient, + final CoreStitchServiceClient serviceClient, final StitchAppClientInfo appInfo, final TaskDispatcher dispatcher); } diff --git a/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/StitchServiceClientImpl.java b/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/StitchServiceClientImpl.java index f225ab574..4ab607084 100644 --- a/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/StitchServiceClientImpl.java +++ b/android/core/src/main/java/com/mongodb/stitch/android/core/services/internal/StitchServiceClientImpl.java @@ -18,28 +18,50 @@ import com.google.android.gms.tasks.Task; import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; -import com.mongodb.stitch.core.auth.internal.StitchAuthRequestClient; -import com.mongodb.stitch.core.services.internal.CoreStitchServiceClientImpl; -import com.mongodb.stitch.core.services.internal.StitchServiceRoutes; +import com.mongodb.stitch.android.core.services.StitchServiceClient; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; import java.util.List; import java.util.concurrent.Callable; import org.bson.codecs.Decoder; import org.bson.codecs.configuration.CodecRegistry; -public final class StitchServiceClientImpl - extends CoreStitchServiceClientImpl implements StitchServiceClient { +public final class StitchServiceClientImpl implements StitchServiceClient { + private final CoreStitchServiceClient proxy; private final TaskDispatcher dispatcher; public StitchServiceClientImpl( - final StitchAuthRequestClient requestClient, - final StitchServiceRoutes routes, - final String name, - final CodecRegistry codecRegistry, - final TaskDispatcher dispatcher) { - super(requestClient, routes, name, codecRegistry); + final CoreStitchServiceClient proxy, + final TaskDispatcher dispatcher + ) { + this.proxy = proxy; this.dispatcher = dispatcher; } + @Override + public Task callFunction( + final String name, final List args) { + return dispatcher.dispatchTask( + new Callable() { + @Override + public Void call() { + proxy.callFunction(name, args); + return null; + } + }); + } + + @Override + public Task callFunction( + final String name, final List args, final Decoder resultDecoder) { + return dispatcher.dispatchTask( + new Callable() { + @Override + public ResultT call() { + return proxy.callFunction(name, args, null, resultDecoder); + } + }); + } + @Override public Task callFunction( final String name, final List args, final Class resultClass) { @@ -47,7 +69,37 @@ public Task callFunction( new Callable() { @Override public ResultT call() { - return callFunctionInternal(name, args, null, resultClass); + return proxy.callFunction(name, args, null, resultClass); + } + }); + } + + @Override + public Task callFunction( + final String name, + final List args, + final Class resultClass, + final CodecRegistry codecRegistry) { + return dispatcher.dispatchTask( + new Callable() { + @Override + public ResultT call() { + return proxy.callFunction(name, args, null, resultClass, codecRegistry); + } + }); + } + + @Override + public Task callFunction( + final String name, + final List args, + final Long requestTimeout) { + return dispatcher.dispatchTask( + new Callable() { + @Override + public Void call() { + proxy.callFunction(name, args, requestTimeout); + return null; } }); } @@ -62,19 +114,22 @@ public Task callFunction( new Callable() { @Override public ResultT call() { - return callFunctionInternal(name, args, requestTimeout, resultClass); + return proxy.callFunction(name, args, requestTimeout, resultClass); } }); } @Override public Task callFunction( - final String name, final List args, final Decoder resultDecoder) { + final String name, + final List args, + final Long requestTimeout, + final Decoder resultDecoder) { return dispatcher.dispatchTask( new Callable() { @Override public ResultT call() { - return callFunctionInternal(name, args, null, resultDecoder); + return proxy.callFunction(name, args, requestTimeout, resultDecoder); } }); } @@ -84,13 +139,29 @@ public Task callFunction( final String name, final List args, final Long requestTimeout, - final Decoder resultDecoder) { + final Class resultClass, + final CodecRegistry codecRegistry) { return dispatcher.dispatchTask( new Callable() { @Override public ResultT call() { - return callFunctionInternal(name, args, requestTimeout, resultDecoder); + return proxy.callFunction( + name, + args, + requestTimeout, + resultClass, + codecRegistry); } }); } + + @Override + public CodecRegistry getCodecRegistry() { + return proxy.getCodecRegistry(); + } + + @Override + public StitchServiceClient withCodecRegistry(final CodecRegistry codecRegistry) { + return new StitchServiceClientImpl(proxy.withCodecRegistry(codecRegistry), dispatcher); + } } diff --git a/android/core/src/main/java/com/mongodb/stitch/android/core/services/package-info.java b/android/core/src/main/java/com/mongodb/stitch/android/core/services/package-info.java new file mode 100644 index 000000000..411f96bef --- /dev/null +++ b/android/core/src/main/java/com/mongodb/stitch/android/core/services/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This package contains the definitions of service specific concepts. + */ +package com.mongodb.stitch.android.core.services; diff --git a/android/services/aws-s3/src/main/java/com/mongodb/stitch/android/services/aws/s3/AwsS3ServiceClient.java b/android/services/aws-s3/src/main/java/com/mongodb/stitch/android/services/aws/s3/AwsS3ServiceClient.java index 0e68486a8..942db4c7d 100644 --- a/android/services/aws-s3/src/main/java/com/mongodb/stitch/android/services/aws/s3/AwsS3ServiceClient.java +++ b/android/services/aws-s3/src/main/java/com/mongodb/stitch/android/services/aws/s3/AwsS3ServiceClient.java @@ -21,19 +21,22 @@ import com.google.android.gms.tasks.Task; import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.android.core.services.internal.NamedServiceClientFactory; -import com.mongodb.stitch.android.core.services.internal.StitchServiceClient; import com.mongodb.stitch.android.services.aws.s3.internal.AwsS3ServiceClientImpl; import com.mongodb.stitch.core.StitchAppClientInfo; import com.mongodb.stitch.core.services.aws.s3.AwsS3PutObjectResult; import com.mongodb.stitch.core.services.aws.s3.AwsS3SignPolicyResult; import com.mongodb.stitch.core.services.aws.s3.internal.CoreAwsS3ServiceClient; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; import java.io.InputStream; import org.bson.types.Binary; /** * The AWS S3 service client. + * + * @deprecated use AwsServiceClient instead. */ +@Deprecated public interface AwsS3ServiceClient { /** @@ -126,7 +129,7 @@ Task signPolicy( new NamedServiceClientFactory() { @Override public AwsS3ServiceClient getClient( - final StitchServiceClient service, + final CoreStitchServiceClient service, final StitchAppClientInfo appInfo, final TaskDispatcher dispatcher ) { diff --git a/android/services/aws-s3/src/main/java/com/mongodb/stitch/android/services/aws/s3/internal/AwsS3ServiceClientImpl.java b/android/services/aws-s3/src/main/java/com/mongodb/stitch/android/services/aws/s3/internal/AwsS3ServiceClientImpl.java index 560bad70d..254727470 100644 --- a/android/services/aws-s3/src/main/java/com/mongodb/stitch/android/services/aws/s3/internal/AwsS3ServiceClientImpl.java +++ b/android/services/aws-s3/src/main/java/com/mongodb/stitch/android/services/aws/s3/internal/AwsS3ServiceClientImpl.java @@ -29,6 +29,10 @@ import java.util.concurrent.Callable; import org.bson.types.Binary; +/** + * @deprecated use AwsServiceClient instead. + */ +@Deprecated public final class AwsS3ServiceClientImpl implements AwsS3ServiceClient { private final CoreAwsS3ServiceClient proxy; diff --git a/android/services/aws-ses/src/main/java/com/mongodb/stitch/android/services/aws/ses/AwsSesServiceClient.java b/android/services/aws-ses/src/main/java/com/mongodb/stitch/android/services/aws/ses/AwsSesServiceClient.java index c970762e5..29c81ef0e 100644 --- a/android/services/aws-ses/src/main/java/com/mongodb/stitch/android/services/aws/ses/AwsSesServiceClient.java +++ b/android/services/aws-ses/src/main/java/com/mongodb/stitch/android/services/aws/ses/AwsSesServiceClient.java @@ -21,15 +21,18 @@ import com.google.android.gms.tasks.Task; import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.android.core.services.internal.NamedServiceClientFactory; -import com.mongodb.stitch.android.core.services.internal.StitchServiceClient; import com.mongodb.stitch.android.services.aws.ses.internal.AwsSesServiceClientImpl; import com.mongodb.stitch.core.StitchAppClientInfo; import com.mongodb.stitch.core.services.aws.ses.AwsSesSendResult; import com.mongodb.stitch.core.services.aws.ses.internal.CoreAwsSesServiceClient; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; /** * The AWS SES service client. + * + * @deprecated use AwsServiceClient instead. */ +@Deprecated public interface AwsSesServiceClient { /** @@ -51,7 +54,7 @@ Task sendEmail( new NamedServiceClientFactory() { @Override public AwsSesServiceClient getClient( - final StitchServiceClient service, + final CoreStitchServiceClient service, final StitchAppClientInfo appInfo, final TaskDispatcher dispatcher ) { diff --git a/android/services/aws-ses/src/main/java/com/mongodb/stitch/android/services/aws/ses/internal/AwsSesServiceClientImpl.java b/android/services/aws-ses/src/main/java/com/mongodb/stitch/android/services/aws/ses/internal/AwsSesServiceClientImpl.java index dc061490a..eaeb40fd2 100644 --- a/android/services/aws-ses/src/main/java/com/mongodb/stitch/android/services/aws/ses/internal/AwsSesServiceClientImpl.java +++ b/android/services/aws-ses/src/main/java/com/mongodb/stitch/android/services/aws/ses/internal/AwsSesServiceClientImpl.java @@ -26,6 +26,10 @@ import java.util.concurrent.Callable; +/** + * @deprecated use AwsServiceClient instead. + */ +@Deprecated public final class AwsSesServiceClientImpl implements AwsSesServiceClient { private final CoreAwsSesServiceClient proxy; diff --git a/android/services/aws/build.gradle b/android/services/aws/build.gradle new file mode 100644 index 000000000..e5b13b02d --- /dev/null +++ b/android/services/aws/build.gradle @@ -0,0 +1,46 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'digital.wup.android-maven-publish' +apply plugin: 'jacoco-android' +apply plugin: 'com.jfrog.bintray' + +ext.pomDisplayName = "Android AWS Service" + +buildscript { + dependencies { + classpath 'com.android.tools.build:gradle:3.1.3' + classpath 'digital.wup:android-maven-publish:3.3.0' + classpath 'com.dicedmelon.gradle:jacoco-android:0.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.0' + } +} + +android { + compileSdkVersion target_api + defaultConfig { + minSdkVersion min_api + targetSdkVersion target_api + + Properties properties = new Properties() + File file = project.rootProject.file('local.properties') + if (file.exists()) { + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + } + if (!properties.getProperty("test.stitch.awsAccessKeyId", "").isEmpty()) { + testInstrumentationRunnerArgument "test.stitch.awsAccessKeyId", properties.getProperty("test.stitch.awsAccessKeyId", "") + } + if (!properties.getProperty("test.stitch.awsSecretAccessKey", "").isEmpty()) { + testInstrumentationRunnerArgument "test.stitch.awsSecretAccessKey", properties.getProperty("test.stitch.awsSecretAccessKey", "") + } + } +} + +dependencies { + implementation project(':android:stitch-android-core') + api project(':core:core-services:stitch-core-services-aws') + + androidTestImplementation project(':android:stitch-android-testutils') + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" +} diff --git a/android/services/aws/src/androidTest/java/com/mongodb/stitch/android/services/aws/AwsServiceClientIntTests.kt b/android/services/aws/src/androidTest/java/com/mongodb/stitch/android/services/aws/AwsServiceClientIntTests.kt new file mode 100644 index 000000000..248eb5266 --- /dev/null +++ b/android/services/aws/src/androidTest/java/com/mongodb/stitch/android/services/aws/AwsServiceClientIntTests.kt @@ -0,0 +1,132 @@ +package com.mongodb.stitch.android.services.aws + +import android.support.test.InstrumentationRegistry +import com.google.android.gms.tasks.Tasks +import com.mongodb.stitch.android.testutils.BaseStitchAndroidIntTest +import com.mongodb.stitch.core.StitchServiceErrorCode +import com.mongodb.stitch.core.StitchServiceException +import com.mongodb.stitch.core.admin.authProviders.ProviderConfigs +import com.mongodb.stitch.core.admin.services.ServiceConfigs +import com.mongodb.stitch.core.admin.services.rules.RuleCreator +import com.mongodb.stitch.core.auth.providers.anonymous.AnonymousCredential +import com.mongodb.stitch.core.internal.common.IoUtils.readAllToString +import com.mongodb.stitch.core.internal.net.Method +import com.mongodb.stitch.core.internal.net.OkHttpTransport +import com.mongodb.stitch.core.internal.net.Request +import com.mongodb.stitch.core.services.aws.AwsRequest +import org.bson.Document +import org.bson.types.Binary +import org.bson.types.ObjectId +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Assume.assumeTrue +import org.junit.Before +import org.junit.Test +import java.nio.charset.StandardCharsets +import java.util.concurrent.ExecutionException + +class AwsServiceClientIntTests : BaseStitchAndroidIntTest() { + + private val awsAccessKeyIdProp = "test.stitch.awsAccessKeyId" + private val awsSecretAccessKeyProp = "test.stitch.awsSecretAccessKey" + + private fun getAwsAccessKeyId(): String { + return InstrumentationRegistry.getArguments().getString(awsAccessKeyIdProp, "") + } + + private fun getAwsSecretAccessKey(): String { + return InstrumentationRegistry.getArguments().getString(awsSecretAccessKeyProp, "") + } + + @Before + override fun setup() { + assumeTrue("no AWS Access Key Id in properties; skipping test", getAwsAccessKeyId().isNotEmpty()) + assumeTrue("no AWS Secret Access Key in properties; skipping test", getAwsSecretAccessKey().isNotEmpty()) + super.setup() + } + + @Test + fun testPutObject() { + val app = createApp() + addProvider(app.second, ProviderConfigs.Anon) + val svc = addService( + app.second, + "aws", + "aws1", + ServiceConfigs.Aws(getAwsAccessKeyId(), getAwsSecretAccessKey())) + addRule(svc.second, RuleCreator.Aws( + "default", + setOf("s3:PutObject"))) + + val client = getAppClient(app.first) + Tasks.await(client.auth.loginWithCredential(AnonymousCredential())) + + val awsS3 = client.getServiceClient(AwsServiceClient.factory, "aws1") + + // Putting to an bad bucket should fail + val bucket = "notmystuff" + val key = ObjectId().toHexString() + val acl = "public-read" + val contentType = "plain/text" + val body = "hello again friend; did you miss me" + val args = Document() + args["Bucket"] = bucket + args["Key"] = key + args["ACL"] = acl + args["ContentType"] = contentType + args["Body"] = body + + try { + Tasks.await(awsS3.execute(AwsRequest.Builder() + .withService("s3") + .withAction("PutObject") + .withArguments(args) + .build())) + fail() + } catch (ex: ExecutionException) { + assertTrue(ex.cause is StitchServiceException) + val svcEx = ex.cause as StitchServiceException + assertEquals(StitchServiceErrorCode.AWS_ERROR, svcEx.errorCode) + } + + // Putting with all good params for S3 should work + val bucketGood = "stitch-test-sdkfiles" + val transport = OkHttpTransport() + + args["Bucket"] = bucketGood + var result = Tasks.await(awsS3.execute(AwsRequest.Builder() + .withService("s3") + .withAction("PutObject") + .withArguments(args) + .build(), + Document::class.java)) + var location = "https://stitch-test-sdkfiles.s3.amazonaws.com/$key" + assertTrue(result.containsKey("ETag")) + + var httpResult = transport.roundTrip(Request.Builder() + .withMethod(Method.GET) + .withUrl(location) + .withTimeout(1500L) + .build()) + assertEquals(body, readAllToString(httpResult.body)) + + val bodyBin = Binary(body.toByteArray(StandardCharsets.UTF_8)) + args["Body"] = bodyBin + Tasks.await(awsS3.execute(AwsRequest.Builder() + .withService("s3") + .withAction("PutObject") + .withArguments(args) + .build(), + Document::class.java)) + assertTrue(result.containsKey("ETag")) + location = "https://stitch-test-sdkfiles.s3.amazonaws.com/$key" + + httpResult = transport.roundTrip(Request.Builder() + .withMethod(Method.GET) + .withUrl(location) + .withTimeout(1500L) + .build()) + assertEquals(body, readAllToString(httpResult.body)) + } +} diff --git a/android/services/aws/src/main/AndroidManifest.xml b/android/services/aws/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a7bc03875 --- /dev/null +++ b/android/services/aws/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/AwsServiceClient.java b/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/AwsServiceClient.java new file mode 100644 index 000000000..81c5148ba --- /dev/null +++ b/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/AwsServiceClient.java @@ -0,0 +1,190 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.stitch.android.services.aws; + +import com.google.android.gms.tasks.Task; +import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; +import com.mongodb.stitch.android.core.services.internal.NamedServiceClientFactory; +import com.mongodb.stitch.android.services.aws.internal.AwsServiceClientImpl; +import com.mongodb.stitch.core.StitchAppClientInfo; +import com.mongodb.stitch.core.services.aws.AwsRequest; +import com.mongodb.stitch.core.services.aws.internal.CoreAwsServiceClient; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; +import javax.annotation.Nonnull; +import org.bson.codecs.Decoder; +import org.bson.codecs.configuration.CodecRegistry; + +/** + * The AWS service client. + */ +public interface AwsServiceClient { + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + * @return a task that completes when the request completes. + */ + Task execute(@Nonnull final AwsRequest request); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Decoder resultDecoder); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Class resultClass); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Class resultClass, + @Nonnull final CodecRegistry codecRegistry + ); + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @return a task that completes when the request completes. + */ + Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout + ); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout, + @Nonnull final Decoder resultDecoder + ); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout, + @Nonnull final Class resultClass + ); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout, + @Nonnull final Class resultClass, + @Nonnull final CodecRegistry codecRegistry + ); + + /** + * Get the codec registry that will be used to decode responses when a codec registry. + * + * @return a task containing the {@link CodecRegistry} + */ + CodecRegistry getCodecRegistry(); + + /** + * Create a new AwsServiceClient instance with a different codec registry. + * + * @param codecRegistry the new {@link CodecRegistry} for the client. + * @return a new AwsServiceClient instance with the different codec registry + */ + AwsServiceClient withCodecRegistry(@Nonnull final CodecRegistry codecRegistry); + + NamedServiceClientFactory factory = + new NamedServiceClientFactory() { + @Override + public AwsServiceClient getClient( + final CoreStitchServiceClient service, + final StitchAppClientInfo appInfo, + final TaskDispatcher dispatcher + ) { + return new AwsServiceClientImpl(new CoreAwsServiceClient(service), dispatcher); + } + }; +} diff --git a/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/internal/AwsServiceClientImpl.java b/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/internal/AwsServiceClientImpl.java new file mode 100644 index 000000000..020bef1a2 --- /dev/null +++ b/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/internal/AwsServiceClientImpl.java @@ -0,0 +1,246 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.stitch.android.services.aws.internal; + +import com.google.android.gms.tasks.Task; +import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; +import com.mongodb.stitch.android.services.aws.AwsServiceClient; +import com.mongodb.stitch.core.services.aws.AwsRequest; +import com.mongodb.stitch.core.services.aws.internal.CoreAwsServiceClient; +import java.util.concurrent.Callable; +import javax.annotation.Nonnull; +import org.bson.codecs.Decoder; +import org.bson.codecs.configuration.CodecRegistry; + +public final class AwsServiceClientImpl implements AwsServiceClient { + + private final CoreAwsServiceClient proxy; + private final TaskDispatcher dispatcher; + + public AwsServiceClientImpl( + final CoreAwsServiceClient client, + final TaskDispatcher dispatcher + ) { + this.proxy = client; + this.dispatcher = dispatcher; + } + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + * @return a task that completes when the request completes. + */ + public Task execute(@Nonnull final AwsRequest request) { + return dispatcher.dispatchTask(new Callable() { + @Override + public Void call() { + proxy.execute(request); + return null; + } + }); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + public Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Decoder resultDecoder) { + return dispatcher.dispatchTask(new Callable() { + @Override + public ResultT call() { + return proxy.execute(request, resultDecoder); + } + }); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + public Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Class resultClass) { + return dispatcher.dispatchTask(new Callable() { + @Override + public ResultT call() { + return proxy.execute(request, resultClass); + } + }); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + public Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Class resultClass, + @Nonnull final CodecRegistry codecRegistry + ) { + return dispatcher.dispatchTask(new Callable() { + @Override + public ResultT call() { + return proxy.execute(request, resultClass, codecRegistry); + } + }); + } + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @return a task that completes when the request completes. + */ + public Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout + ) { + return dispatcher.dispatchTask(new Callable() { + @Override + public Void call() { + proxy.execute(request, requestTimeout); + return null; + } + }); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + public Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout, + @Nonnull final Decoder resultDecoder + ) { + return dispatcher.dispatchTask(new Callable() { + @Override + public ResultT call() { + return proxy.execute(request, requestTimeout, resultDecoder); + } + }); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + public Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout, + @Nonnull final Class resultClass + ) { + return dispatcher.dispatchTask(new Callable() { + @Override + public ResultT call() { + return proxy.execute(request, requestTimeout, resultClass); + } + }); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return a task containing the decoded result value. + */ + public Task execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout, + @Nonnull final Class resultClass, + @Nonnull final CodecRegistry codecRegistry + ) { + return dispatcher.dispatchTask(new Callable() { + @Override + public ResultT call() { + return proxy.execute(request, requestTimeout, resultClass, codecRegistry); + } + }); + } + + /** + * Get the codec registry that will be used to decode responses when a codec registry. + * + * @return a task containing the {@link CodecRegistry} + */ + public CodecRegistry getCodecRegistry() { + return proxy.getCodecRegistry(); + } + + /** + * Create a new AwsServiceClient instance with a different codec registry. + * + * @param codecRegistry the new {@link CodecRegistry} for the client. + * @return a new AwsServiceClient instance with the different codec registry + */ + public AwsServiceClient withCodecRegistry(@Nonnull final CodecRegistry codecRegistry) { + return new AwsServiceClientImpl(proxy.withCodecRegistry(codecRegistry), dispatcher); + } +} diff --git a/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/internal/package-info.java b/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/internal/package-info.java new file mode 100644 index 000000000..ccdd2788d --- /dev/null +++ b/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/internal/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This internal package contains the Android SDK's implementation of the AWS service client. + */ +package com.mongodb.stitch.android.services.aws.internal; diff --git a/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/package-info.java b/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/package-info.java new file mode 100644 index 000000000..2de221390 --- /dev/null +++ b/android/services/aws/src/main/java/com/mongodb/stitch/android/services/aws/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** This package contains the AWS service client. */ +package com.mongodb.stitch.android.services.aws; diff --git a/android/services/aws/src/main/res/values/strings.xml b/android/services/aws/src/main/res/values/strings.xml new file mode 100644 index 000000000..854200555 --- /dev/null +++ b/android/services/aws/src/main/res/values/strings.xml @@ -0,0 +1,2 @@ + + diff --git a/android/services/fcm/src/main/java/com/mongodb/stitch/android/services/fcm/FcmServiceClient.java b/android/services/fcm/src/main/java/com/mongodb/stitch/android/services/fcm/FcmServiceClient.java index d0899e1c3..8a0033892 100644 --- a/android/services/fcm/src/main/java/com/mongodb/stitch/android/services/fcm/FcmServiceClient.java +++ b/android/services/fcm/src/main/java/com/mongodb/stitch/android/services/fcm/FcmServiceClient.java @@ -19,12 +19,13 @@ import com.google.android.gms.tasks.Task; import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.android.core.services.internal.NamedServiceClientFactory; -import com.mongodb.stitch.android.core.services.internal.StitchServiceClient; import com.mongodb.stitch.android.services.fcm.internal.FcmServiceClientImpl; import com.mongodb.stitch.core.StitchAppClientInfo; import com.mongodb.stitch.core.services.fcm.FcmSendMessageRequest; import com.mongodb.stitch.core.services.fcm.FcmSendMessageResult; import com.mongodb.stitch.core.services.fcm.internal.CoreFcmServiceClient; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; + import java.util.Collection; /** @@ -69,7 +70,7 @@ Task sendMessageToRegistrationTokens( new NamedServiceClientFactory() { @Override public FcmServiceClient getClient( - final StitchServiceClient service, + final CoreStitchServiceClient service, final StitchAppClientInfo appInfo, final TaskDispatcher dispatcher ) { diff --git a/android/services/http/src/androidTest/java/com/mongodb/stitch/android/services/http/HttpServiceClientIntTests.kt b/android/services/http/src/androidTest/java/com/mongodb/stitch/android/services/http/HttpServiceClientIntTests.kt index 6e2750356..e3f37ce04 100644 --- a/android/services/http/src/androidTest/java/com/mongodb/stitch/android/services/http/HttpServiceClientIntTests.kt +++ b/android/services/http/src/androidTest/java/com/mongodb/stitch/android/services/http/HttpServiceClientIntTests.kt @@ -113,7 +113,7 @@ class HttpServiceClientIntTests : BaseStitchAndroidIntTest() { assertEquals("200 OK", response.status) assertEquals(200, response.statusCode) - assertTrue(response.contentLength in 300..400) + assertTrue(response.contentLength in 300..500) Assert.assertNotNull(response.body) val dataDoc = Document.parse(String(response.body!!)) assertTrue(Arrays.equals( diff --git a/android/services/http/src/main/java/com/mongodb/stitch/android/services/http/HttpServiceClient.java b/android/services/http/src/main/java/com/mongodb/stitch/android/services/http/HttpServiceClient.java index e33296994..e412c068d 100644 --- a/android/services/http/src/main/java/com/mongodb/stitch/android/services/http/HttpServiceClient.java +++ b/android/services/http/src/main/java/com/mongodb/stitch/android/services/http/HttpServiceClient.java @@ -21,12 +21,12 @@ import com.google.android.gms.tasks.Task; import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.android.core.services.internal.NamedServiceClientFactory; -import com.mongodb.stitch.android.core.services.internal.StitchServiceClient; import com.mongodb.stitch.android.services.http.internal.HttpServiceClientImpl; import com.mongodb.stitch.core.StitchAppClientInfo; import com.mongodb.stitch.core.services.http.HttpRequest; import com.mongodb.stitch.core.services.http.HttpResponse; import com.mongodb.stitch.core.services.http.internal.CoreHttpServiceClient; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; /** * The HTTP service client. @@ -45,7 +45,7 @@ public interface HttpServiceClient { new NamedServiceClientFactory() { @Override public HttpServiceClient getClient( - final StitchServiceClient service, + final CoreStitchServiceClient service, final StitchAppClientInfo appInfo, final TaskDispatcher dispatcher ) { diff --git a/android/services/mongodb-local/src/main/java/com/mongodb/stitch/android/services/mongodb/local/LocalMongoDbService.java b/android/services/mongodb-local/src/main/java/com/mongodb/stitch/android/services/mongodb/local/LocalMongoDbService.java index a7ed29d87..b05798d90 100644 --- a/android/services/mongodb-local/src/main/java/com/mongodb/stitch/android/services/mongodb/local/LocalMongoDbService.java +++ b/android/services/mongodb-local/src/main/java/com/mongodb/stitch/android/services/mongodb/local/LocalMongoDbService.java @@ -21,9 +21,9 @@ import com.mongodb.client.MongoClient; import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.android.core.services.internal.ServiceClientFactory; -import com.mongodb.stitch.android.core.services.internal.StitchServiceClient; import com.mongodb.stitch.android.services.mongodb.local.internal.MongoDbMobileProvider; import com.mongodb.stitch.core.StitchAppClientInfo; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; import com.mongodb.stitch.core.services.mongodb.local.internal.CoreLocalMongoDbService; import org.bson.Document; @@ -42,7 +42,7 @@ public final class LocalMongoDbService extends CoreLocalMongoDbService { @Override public synchronized MongoClient getClient( - final StitchServiceClient stitchService, + final CoreStitchServiceClient stitchService, final StitchAppClientInfo appInfo, final TaskDispatcher dispatcher ) { diff --git a/android/services/mongodb-remote/src/main/java/com/mongodb/stitch/android/services/mongodb/remote/RemoteMongoClient.java b/android/services/mongodb-remote/src/main/java/com/mongodb/stitch/android/services/mongodb/remote/RemoteMongoClient.java index fd985193b..76af82db1 100644 --- a/android/services/mongodb-remote/src/main/java/com/mongodb/stitch/android/services/mongodb/remote/RemoteMongoClient.java +++ b/android/services/mongodb-remote/src/main/java/com/mongodb/stitch/android/services/mongodb/remote/RemoteMongoClient.java @@ -18,9 +18,9 @@ import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.android.core.services.internal.NamedServiceClientFactory; -import com.mongodb.stitch.android.core.services.internal.StitchServiceClient; import com.mongodb.stitch.android.services.mongodb.remote.internal.RemoteMongoClientImpl; import com.mongodb.stitch.core.StitchAppClientInfo; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; import com.mongodb.stitch.core.services.mongodb.remote.internal.CoreRemoteMongoClientImpl; /** @@ -40,7 +40,7 @@ public interface RemoteMongoClient { new NamedServiceClientFactory() { @Override public RemoteMongoClient getClient( - final StitchServiceClient service, + final CoreStitchServiceClient service, final StitchAppClientInfo appInfo, final TaskDispatcher dispatcher ) { diff --git a/android/services/twilio/src/main/java/com/mongodb/stitch/android/services/twilio/TwilioServiceClient.java b/android/services/twilio/src/main/java/com/mongodb/stitch/android/services/twilio/TwilioServiceClient.java index e2b1b90ca..e0f262606 100644 --- a/android/services/twilio/src/main/java/com/mongodb/stitch/android/services/twilio/TwilioServiceClient.java +++ b/android/services/twilio/src/main/java/com/mongodb/stitch/android/services/twilio/TwilioServiceClient.java @@ -21,9 +21,9 @@ import com.google.android.gms.tasks.Task; import com.mongodb.stitch.android.core.internal.common.TaskDispatcher; import com.mongodb.stitch.android.core.services.internal.NamedServiceClientFactory; -import com.mongodb.stitch.android.core.services.internal.StitchServiceClient; import com.mongodb.stitch.android.services.twilio.internal.TwilioServiceClientImpl; import com.mongodb.stitch.core.StitchAppClientInfo; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; import com.mongodb.stitch.core.services.twilio.internal.CoreTwilioServiceClient; /** @@ -63,7 +63,7 @@ Task sendMessage( new NamedServiceClientFactory() { @Override public TwilioServiceClient getClient( - final StitchServiceClient service, + final CoreStitchServiceClient service, final StitchAppClientInfo appInfo, final TaskDispatcher dispatcher ) { diff --git a/core/admin-client/src/main/java/com/mongodb/stitch/core/admin/services/ServiceConfigs.kt b/core/admin-client/src/main/java/com/mongodb/stitch/core/admin/services/ServiceConfigs.kt index 268e73f5c..229815611 100644 --- a/core/admin-client/src/main/java/com/mongodb/stitch/core/admin/services/ServiceConfigs.kt +++ b/core/admin-client/src/main/java/com/mongodb/stitch/core/admin/services/ServiceConfigs.kt @@ -3,6 +3,7 @@ package com.mongodb.stitch.core.admin.services import com.fasterxml.jackson.annotation.JsonProperty sealed class ServiceConfigs { + data class Aws(val accessKeyId: String, val secretAccessKey: String) : ServiceConfigs() data class AwsS3(val region: String, val accessKeyId: String, val secretAccessKey: String) : ServiceConfigs() data class AwsSes(val region: String, val accessKeyId: String, val secretAccessKey: String) : ServiceConfigs() data class Fcm(@JsonProperty("senderId") val senderId: String, @JsonProperty("apiKey") val apiKey: String) : ServiceConfigs() diff --git a/core/admin-client/src/main/java/com/mongodb/stitch/core/admin/services/rules/RulesResources.kt b/core/admin-client/src/main/java/com/mongodb/stitch/core/admin/services/rules/RulesResources.kt index 0f7844af2..e46d98586 100644 --- a/core/admin-client/src/main/java/com/mongodb/stitch/core/admin/services/rules/RulesResources.kt +++ b/core/admin-client/src/main/java/com/mongodb/stitch/core/admin/services/rules/RulesResources.kt @@ -32,6 +32,7 @@ enum class FcmActions { } sealed class RuleCreator { + data class Aws(val name: String, val actions: Set) : RuleCreator() data class AwsS3(val name: String, val actions: Set) : RuleCreator() data class AwsSes(val name: String, val actions: Set) : RuleCreator() data class Fcm(val name: String, val actions: Set) : RuleCreator() diff --git a/core/sdk/src/main/java/com/mongodb/stitch/core/internal/CoreStitchAppClient.java b/core/sdk/src/main/java/com/mongodb/stitch/core/internal/CoreStitchAppClient.java index d64d43727..43530c220 100644 --- a/core/sdk/src/main/java/com/mongodb/stitch/core/internal/CoreStitchAppClient.java +++ b/core/sdk/src/main/java/com/mongodb/stitch/core/internal/CoreStitchAppClient.java @@ -54,11 +54,11 @@ public CoreStitchAppClient( * @param requestTimeout the number of milliseconds the client should wait for a response from the * server before failing with an error. */ - public void callFunctionInternal( + public void callFunction( final String name, final List args, final @Nullable Long requestTimeout) { - this.functionService.callFunctionInternal(name, args, requestTimeout); + this.functionService.callFunction(name, args, requestTimeout); } /** @@ -73,12 +73,12 @@ public void callFunctionInternal( * @param the type into which the Stitch response will be decoded. * @return the decoded value. */ - public T callFunctionInternal( + public T callFunction( final String name, final List args, final @Nullable Long requestTimeout, final Decoder decoder) { - return this.functionService.callFunctionInternal(name, args, requestTimeout, decoder); + return this.functionService.callFunction(name, args, requestTimeout, decoder); } /** @@ -96,12 +96,12 @@ public T callFunctionInternal( * @param the type into which the Stitch response will be decoded. * @return the decoded value. */ - public T callFunctionInternal( + public T callFunction( final String name, final List args, final @Nullable Long requestTimeout, final Class resultClass) { - return this.functionService.callFunctionInternal(name, args, requestTimeout, resultClass); + return this.functionService.callFunction(name, args, requestTimeout, resultClass); } /** @@ -117,7 +117,7 @@ public T callFunctionInternal( * @param codecRegistry the codec registry that will be used to encode/decode the function call. * @return the decoded value. */ - public T callFunctionInternal( + public T callFunction( final String name, final List args, final @Nullable Long requestTimeout, @@ -126,6 +126,6 @@ public T callFunctionInternal( ) { return this.functionService .withCodecRegistry(codecRegistry) - .callFunctionInternal(name, args, requestTimeout, resultClass); + .callFunction(name, args, requestTimeout, resultClass); } } diff --git a/core/sdk/src/main/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceClient.java b/core/sdk/src/main/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceClient.java index 63d30e822..1b2039893 100644 --- a/core/sdk/src/main/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceClient.java +++ b/core/sdk/src/main/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceClient.java @@ -23,37 +23,50 @@ public interface CoreStitchServiceClient { - void callFunctionInternal( + void callFunction( final String name, final List args); - T callFunctionInternal( + T callFunction( final String name, final List args, final Decoder resultDecoder); - T callFunctionInternal( + T callFunction( final String name, final List args, final Class resultClass); - void callFunctionInternal( + T callFunction( + final String name, + final List args, + final Class resultClass, + final CodecRegistry codecRegistry); + + void callFunction( final String name, final List args, final @Nullable Long requestTimeout); - T callFunctionInternal( + T callFunction( final String name, final List args, final @Nullable Long requestTimeout, final Decoder resultDecoder); - T callFunctionInternal( + T callFunction( final String name, final List args, final @Nullable Long requestTimeout, final Class resultClass); + T callFunction( + final String name, + final List args, + final @Nullable Long requestTimeout, + final Class resultClass, + final CodecRegistry codecRegistry); + CodecRegistry getCodecRegistry(); CoreStitchServiceClient withCodecRegistry(final CodecRegistry codecRegistry); diff --git a/core/sdk/src/main/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceClientImpl.java b/core/sdk/src/main/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceClientImpl.java index e54ddf61e..4fc32c8de 100644 --- a/core/sdk/src/main/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceClientImpl.java +++ b/core/sdk/src/main/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceClientImpl.java @@ -72,14 +72,14 @@ private StitchAuthDocRequest getCallServiceFunctionRequest( return reqBuilder.build(codecRegistry); } - public void callFunctionInternal( + public void callFunction( final String name, final List args ) { requestClient.doAuthenticatedRequest(getCallServiceFunctionRequest(name, args, null)); } - public T callFunctionInternal( + public T callFunction( final String name, final List args, final Decoder resultDecoder) { @@ -87,7 +87,7 @@ public T callFunctionInternal( getCallServiceFunctionRequest(name, args, null), resultDecoder); } - public T callFunctionInternal( + public T callFunction( final String name, final List args, final Class resultClass) { @@ -95,7 +95,16 @@ public T callFunctionInternal( getCallServiceFunctionRequest(name, args, null), resultClass, codecRegistry); } - public void callFunctionInternal( + public T callFunction( + final String name, + final List args, + final Class resultClass, + final CodecRegistry codecRegistry) { + return requestClient.doAuthenticatedRequest( + getCallServiceFunctionRequest(name, args, null), resultClass, codecRegistry); + } + + public void callFunction( final String name, final List args, final @Nullable Long requestTimeout @@ -103,7 +112,7 @@ public void callFunctionInternal( requestClient.doAuthenticatedRequest(getCallServiceFunctionRequest(name, args, requestTimeout)); } - public T callFunctionInternal( + public T callFunction( final String name, final List args, final @Nullable Long requestTimeout, @@ -112,7 +121,7 @@ public T callFunctionInternal( getCallServiceFunctionRequest(name, args, requestTimeout), resultDecoder); } - public T callFunctionInternal( + public T callFunction( final String name, final List args, final @Nullable Long requestTimeout, @@ -121,6 +130,16 @@ public T callFunctionInternal( getCallServiceFunctionRequest(name, args, requestTimeout), resultClass, codecRegistry); } + public T callFunction( + final String name, + final List args, + final @Nullable Long requestTimeout, + final Class resultClass, + final CodecRegistry codecRegistry) { + return requestClient.doAuthenticatedRequest( + getCallServiceFunctionRequest(name, args, requestTimeout), resultClass, codecRegistry); + } + public CodecRegistry getCodecRegistry() { return codecRegistry; } diff --git a/core/sdk/src/test/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceUnitTests.java b/core/sdk/src/test/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceUnitTests.java index 92c7863d6..116512c6c 100644 --- a/core/sdk/src/test/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceUnitTests.java +++ b/core/sdk/src/test/java/com/mongodb/stitch/core/services/internal/CoreStitchServiceUnitTests.java @@ -42,7 +42,7 @@ public class CoreStitchServiceUnitTests { @Test - public void testCallFunctionInternal() { + public void testCallFunction() { final String serviceName = "svc1"; final StitchServiceRoutes routes = new StitchServiceRoutes("foo"); final StitchAuthRequestClient requestClient = Mockito.mock(StitchAuthRequestClient.class); @@ -72,9 +72,9 @@ public void testCallFunctionInternal() { expectedRequestDoc.put("arguments", args); assertEquals( - 42, (int) coreStitchService.callFunctionInternal( + 42, (int) coreStitchService.callFunction( funcName, args, null, new IntegerCodec())); - assertEquals(42, (int) coreStitchService.callFunctionInternal( + assertEquals(42, (int) coreStitchService.callFunction( funcName, args, null, Integer.class)); final ArgumentCaptor docArgument = diff --git a/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/AwsS3PutObjectResult.java b/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/AwsS3PutObjectResult.java index a8ff1b644..2c5be5957 100644 --- a/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/AwsS3PutObjectResult.java +++ b/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/AwsS3PutObjectResult.java @@ -18,7 +18,10 @@ /** * The result of an AWS S3 put object request. + * + * @deprecated use AwsServiceClient instead. */ +@Deprecated public class AwsS3PutObjectResult { private final String location; diff --git a/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/AwsS3SignPolicyResult.java b/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/AwsS3SignPolicyResult.java index a35ba2918..ddccd38bd 100644 --- a/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/AwsS3SignPolicyResult.java +++ b/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/AwsS3SignPolicyResult.java @@ -18,7 +18,10 @@ /** * The result of an AWS S3 sign policy request. + * + * @deprecated use AwsServiceClient instead. */ +@Deprecated public class AwsS3SignPolicyResult { private final String policy; private final String signature; diff --git a/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/internal/CoreAwsS3ServiceClient.java b/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/internal/CoreAwsS3ServiceClient.java index 7b74aa6be..51fabaf0b 100644 --- a/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/internal/CoreAwsS3ServiceClient.java +++ b/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/internal/CoreAwsS3ServiceClient.java @@ -27,6 +27,10 @@ import org.bson.Document; import org.bson.types.Binary; +/** + * @deprecated use AwsServiceClient instead. + */ +@Deprecated public class CoreAwsS3ServiceClient { private final CoreStitchServiceClient service; @@ -48,7 +52,7 @@ private AwsS3PutObjectResult putObjectInternal( args.put(PutAction.ACL_PARAM, acl); args.put(PutAction.CONTENT_TYPE_PARAM, contentType); args.put(PutAction.BODY_PARAM, body); - return service.callFunctionInternal( + return service.callFunction( PutAction.ACTION_NAME, Collections.singletonList(args), ResultDecoders.putObjectResultDecoder); @@ -105,7 +109,7 @@ public AwsS3SignPolicyResult signPolicy( args.put(SignPolicyAction.KEY_PARAM, key); args.put(SignPolicyAction.ACL_PARAM, acl); args.put(SignPolicyAction.CONTENT_TYPE_PARAM, contentType); - return service.callFunctionInternal( + return service.callFunction( SignPolicyAction.ACTION_NAME, Collections.singletonList(args), ResultDecoders.signPolicyResultDecoder); diff --git a/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/internal/ResultDecoders.java b/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/internal/ResultDecoders.java index 38785e545..787ab9059 100644 --- a/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/internal/ResultDecoders.java +++ b/core/services/aws-s3/src/main/java/com/mongodb/stitch/core/services/aws/s3/internal/ResultDecoders.java @@ -26,6 +26,10 @@ import org.bson.codecs.DecoderContext; import org.bson.codecs.DocumentCodec; +/** + * @deprecated use AwsServiceClient instead. + */ +@Deprecated class ResultDecoders { static final Decoder putObjectResultDecoder = diff --git a/core/services/aws-s3/src/test/java/com/mongodb/stitch/core/services/aws/s3/internal/CoreAwsS3ServiceClientUnitTests.java b/core/services/aws-s3/src/test/java/com/mongodb/stitch/core/services/aws/s3/internal/CoreAwsS3ServiceClientUnitTests.java index 718b88beb..96afebddd 100644 --- a/core/services/aws-s3/src/test/java/com/mongodb/stitch/core/services/aws/s3/internal/CoreAwsS3ServiceClientUnitTests.java +++ b/core/services/aws-s3/src/test/java/com/mongodb/stitch/core/services/aws/s3/internal/CoreAwsS3ServiceClientUnitTests.java @@ -56,7 +56,7 @@ public void testPutObject() throws IOException { final String expectedLocation = "awsLocation"; Mockito.doReturn(new AwsS3PutObjectResult(expectedLocation)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); AwsS3PutObjectResult result = client.putObject(bucket, key, acl, contentType, body); @@ -67,7 +67,7 @@ public void testPutObject() throws IOException { final ArgumentCaptor> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -89,7 +89,7 @@ public void testPutObject() throws IOException { assertEquals(result.getLocation(), expectedLocation); verify(service, times(2)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -105,7 +105,7 @@ public void testPutObject() throws IOException { assertEquals(result.getLocation(), expectedLocation); verify(service, times(3)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -121,7 +121,7 @@ public void testPutObject() throws IOException { assertEquals(result.getLocation(), expectedLocation); verify(service, times(4)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -133,7 +133,7 @@ public void testPutObject() throws IOException { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> client.putObject(bucket, key, acl, contentType, body), IllegalArgumentException.class); } @@ -161,7 +161,7 @@ public void testSignPolicy() { expectedAlgorithm, expectedDate, expectedCredential)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final AwsS3SignPolicyResult result = client.signPolicy(bucket, key, acl, contentType); @@ -177,7 +177,7 @@ public void testSignPolicy() { final ArgumentCaptor> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -194,7 +194,7 @@ public void testSignPolicy() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> client.signPolicy(bucket, key, acl, contentType), IllegalArgumentException.class); } diff --git a/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/AwsSesSendResult.java b/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/AwsSesSendResult.java index 7a108b087..4972d9da1 100644 --- a/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/AwsSesSendResult.java +++ b/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/AwsSesSendResult.java @@ -26,7 +26,10 @@ /** * The result of an AWS SES send request. + * + * @deprecated use AwsServiceClient instead. */ +@Deprecated public class AwsSesSendResult { private final String messageId; diff --git a/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/internal/CoreAwsSesServiceClient.java b/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/internal/CoreAwsSesServiceClient.java index b84c07f01..b4ff8ef78 100644 --- a/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/internal/CoreAwsSesServiceClient.java +++ b/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/internal/CoreAwsSesServiceClient.java @@ -22,6 +22,10 @@ import java.util.Collections; import org.bson.Document; +/** + * @deprecated use AwsServiceClient instead. + */ +@Deprecated public class CoreAwsSesServiceClient { private final CoreStitchServiceClient service; @@ -41,7 +45,7 @@ public AwsSesSendResult sendEmail( args.put("fromAddress", fromAddress); args.put("subject", subject); args.put("body", body); - return service.callFunctionInternal( + return service.callFunction( "send", Collections.singletonList(args), ResultDecoders.sendResultDecoder); diff --git a/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/internal/ResultDecoders.java b/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/internal/ResultDecoders.java index 31b6166af..cd64efb23 100644 --- a/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/internal/ResultDecoders.java +++ b/core/services/aws-ses/src/main/java/com/mongodb/stitch/core/services/aws/ses/internal/ResultDecoders.java @@ -25,6 +25,10 @@ import org.bson.codecs.DecoderContext; import org.bson.codecs.DocumentCodec; +/** + * @deprecated use AwsServiceClient instead. + */ +@Deprecated class ResultDecoders { static final Decoder sendResultDecoder = new AwsSesSendResultDecoder(); diff --git a/core/services/aws-ses/src/test/java/com/mongodb/stitch/core/services/aws/ses/internal/CoreAwsSesServiceClientUnitTests.java b/core/services/aws-ses/src/test/java/com/mongodb/stitch/core/services/aws/ses/internal/CoreAwsSesServiceClientUnitTests.java index 55d2aaf17..d89bcdca9 100644 --- a/core/services/aws-ses/src/test/java/com/mongodb/stitch/core/services/aws/ses/internal/CoreAwsSesServiceClientUnitTests.java +++ b/core/services/aws-ses/src/test/java/com/mongodb/stitch/core/services/aws/ses/internal/CoreAwsSesServiceClientUnitTests.java @@ -49,7 +49,7 @@ public void testSendMessage() { Mockito.doReturn(new AwsSesSendResult(expectedMessageId)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final AwsSesSendResult result = client.sendEmail(to, from, subject, body); assertEquals(result.getMessageId(), expectedMessageId); @@ -59,7 +59,7 @@ public void testSendMessage() { final ArgumentCaptor> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -76,7 +76,7 @@ public void testSendMessage() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> client.sendEmail(to, from, subject, body), IllegalArgumentException.class); } diff --git a/core/services/aws/build.gradle b/core/services/aws/build.gradle new file mode 100644 index 000000000..e8e8170c1 --- /dev/null +++ b/core/services/aws/build.gradle @@ -0,0 +1,22 @@ +apply plugin: 'java-library' +apply plugin: 'com.jfrog.bintray' + +ext.pomDisplayName = "Core AWS Service" + +buildscript { + dependencies { + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.0' + } +} + +dependencies { + implementation project(':core:stitch-core-sdk') + + testImplementation project(':core:stitch-core-admin-client') + testImplementation project(':core:stitch-core-testutils') + testImplementation 'junit:junit:4.12' + testImplementation "org.mockito:mockito-core:2.18.3" +} + +sourceCompatibility = JavaVersion.VERSION_1_7 +targetCompatibility = JavaVersion.VERSION_1_7 diff --git a/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/AwsRequest.java b/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/AwsRequest.java new file mode 100644 index 000000000..61b97201e --- /dev/null +++ b/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/AwsRequest.java @@ -0,0 +1,157 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.stitch.core.services.aws; + +import org.bson.Document; + +/** + * An AwsRequest encapsulates the details of an AWS request over the AWS service. + */ +public final class AwsRequest { + private final String service; + private final String action; + private final String region; + private final Document arguments; + + private AwsRequest( + final String service, + final String action, + final String region, + final Document arguments + ) { + this.service = service; + this.action = action; + this.region = region; + this.arguments = arguments; + } + + /** + * Returns the AWS service that the action in the request will be performed against. + * + * @return the AWS service that the action in the request will be performed against. + */ + public String getService() { + return service; + } + + /** + * Returns the action within the AWS service to perform. + * + * @return the action within the AWS service to perform. + */ + public String getAction() { + return action; + } + + /** + * Returns the region that service in this request should be scoped to. + * + * @return the region that service in this request should be scoped to. + */ + public String getRegion() { + return region; + } + + /** + * Returns the arguments that will be used in the action. + * + * @return the arguments that will be used in the action. + */ + public Document getArguments() { + return arguments; + } + + /** + * A builder that can build {@link AwsRequest}s. + */ + public static class Builder { + private String service; + private String action; + private String region; + private Document arguments; + + /** + * Constructs a new builder for an AWS request. + */ + public Builder() {} + + /** + * Sets the AWS service that the action in the request will be performed against. + * + * @param service the AWS service that the action in the request will be performed against. + * @return the builder. + */ + public Builder withService(final String service) { + this.service = service; + return this; + } + + /** + * Sets the action within the AWS service to perform. + * + * @param action the action within the AWS service to perform. + * @return the builder. + */ + public Builder withAction(final String action) { + this.action = action; + return this; + } + + /** + * Sets the region that service in this request should be scoped to. + * + * @param region the region that service in this request should be scoped to. + * @return the builder. + */ + public Builder withRegion(final String region) { + this.region = region; + return this; + } + + /** + * Sets the arguments that will be used in the action. + * + * @param arguments the arguments that will be used in the action. + * @return the builder. + */ + public Builder withArguments(final Document arguments) { + this.arguments = arguments; + return this; + } + + /** + * Builds, validates, and returns the {@link AwsRequest}. + * + * @return the built AWS request. + */ + public AwsRequest build() { + if (service == null || service.isEmpty()) { + throw new IllegalArgumentException("must set service"); + } + + if (action == null || action.isEmpty()) { + throw new IllegalArgumentException("must set action"); + } + + return new AwsRequest( + service, + action, + region, + arguments == null ? new Document() : arguments); + } + } +} diff --git a/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/internal/CoreAwsServiceClient.java b/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/internal/CoreAwsServiceClient.java new file mode 100644 index 000000000..3156a133e --- /dev/null +++ b/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/internal/CoreAwsServiceClient.java @@ -0,0 +1,237 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.stitch.core.services.aws.internal; + +import com.mongodb.stitch.core.services.aws.AwsRequest; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; +import java.util.Collections; +import org.bson.Document; +import org.bson.codecs.Decoder; +import org.bson.codecs.configuration.CodecRegistry; + +public class CoreAwsServiceClient { + + private final CoreStitchServiceClient service; + + public CoreAwsServiceClient(final CoreStitchServiceClient service) { + this.service = service; + } + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + */ + public void execute(final AwsRequest request) { + service.callFunction( + Fields.EXECUTE_ACTION_NAME, + Collections.singletonList(getRequestArgs(request))); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute(final AwsRequest request, final Decoder resultDecoder) { + return service.callFunction( + Fields.EXECUTE_ACTION_NAME, + Collections.singletonList(getRequestArgs(request)), + resultDecoder); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute(final AwsRequest request, final Class resultClass) { + return service.callFunction( + Fields.EXECUTE_ACTION_NAME, + Collections.singletonList(getRequestArgs(request)), + resultClass); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute( + final AwsRequest request, + final Class resultClass, + final CodecRegistry codecRegistry + ) { + return service.callFunction( + Fields.EXECUTE_ACTION_NAME, + Collections.singletonList(getRequestArgs(request)), + resultClass, + codecRegistry); + } + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + */ + public void execute( + final AwsRequest request, + final Long requestTimeout + ) { + service.callFunction( + Fields.EXECUTE_ACTION_NAME, + Collections.singletonList(getRequestArgs(request)), + requestTimeout); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute( + final AwsRequest request, + final Long requestTimeout, + final Decoder resultDecoder + ) { + return service.callFunction( + Fields.EXECUTE_ACTION_NAME, + Collections.singletonList(getRequestArgs(request)), + requestTimeout, + resultDecoder); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute( + final AwsRequest request, + final Long requestTimeout, + final Class resultClass + ) { + return service.callFunction( + Fields.EXECUTE_ACTION_NAME, + Collections.singletonList(getRequestArgs(request)), + requestTimeout, + resultClass); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute( + final AwsRequest request, + final Long requestTimeout, + final Class resultClass, + final CodecRegistry codecRegistry + ) { + return service.callFunction( + Fields.EXECUTE_ACTION_NAME, + Collections.singletonList(getRequestArgs(request)), + resultClass, + codecRegistry); + } + + /** + * Get the codec registry that will be used to decode responses when a codec registry. + * + * @return the {@link CodecRegistry} + */ + public CodecRegistry getCodecRegistry() { + return service.getCodecRegistry(); + } + + /** + * Create a new CoreAwsServiceClient instance with a different codec registry. + * + * @param codecRegistry the new {@link CodecRegistry} for the client. + * @return a new CoreAwsServiceClient instance with the different codec registry + */ + public CoreAwsServiceClient withCodecRegistry(final CodecRegistry codecRegistry) { + return new CoreAwsServiceClient(service.withCodecRegistry(codecRegistry)); + } + + private Document getRequestArgs(final AwsRequest request) { + final Document args = new Document(); + args.put(Fields.SERVICE_PARAM, request.getService()); + args.put(Fields.ACTION_PARAM, request.getAction()); + args.put(Fields.ARGUMENTS_PARAM, request.getArguments()); + + if (request.getRegion() != null) { + args.put(Fields.REGION_PARAM, request.getRegion()); + } + return args; + } + + private static class Fields { + static final String EXECUTE_ACTION_NAME = "execute"; + static final String SERVICE_PARAM = "aws_service"; + static final String ACTION_PARAM = "aws_action"; + static final String REGION_PARAM = "aws_region"; + static final String ARGUMENTS_PARAM = "aws_arguments"; + } +} diff --git a/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/internal/package-info.java b/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/internal/package-info.java new file mode 100644 index 000000000..e5ed70339 --- /dev/null +++ b/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/internal/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This internal package contains the base implementation of the AWS service client. + */ +package com.mongodb.stitch.core.services.aws.internal; diff --git a/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/package-info.java b/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/package-info.java new file mode 100644 index 000000000..9aa9b3f7d --- /dev/null +++ b/core/services/aws/src/main/java/com/mongodb/stitch/core/services/aws/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This package contains the classes needed to make requests and receive results with the AWS + * service client. + */ +package com.mongodb.stitch.core.services.aws; diff --git a/core/services/aws/src/test/java/com/mongodb/stitch/core/services/aws/AwsRequestUnitTests.java b/core/services/aws/src/test/java/com/mongodb/stitch/core/services/aws/AwsRequestUnitTests.java new file mode 100644 index 000000000..40155dc32 --- /dev/null +++ b/core/services/aws/src/test/java/com/mongodb/stitch/core/services/aws/AwsRequestUnitTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.stitch.core.services.aws; + +import static com.mongodb.stitch.core.testutils.Assert.assertThrows; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.bson.Document; +import org.junit.Test; + +public class AwsRequestUnitTests { + + @Test + public void testBuilder() { + + // Require at a minimum service and action + assertThrows(() -> new AwsRequest.Builder().build(), IllegalArgumentException.class); + assertThrows(() -> new AwsRequest.Builder() + .withService("ses").build(), IllegalArgumentException.class); + assertThrows(() -> new AwsRequest.Builder() + .withAction("send").build(), IllegalArgumentException.class); + + // Minimum satisfied + final String expectedService = "ses"; + final String expectedAction = "send"; + final AwsRequest request = new AwsRequest.Builder() + .withService(expectedService).withAction(expectedAction).build(); + assertEquals(expectedService, request.getService()); + assertEquals(expectedAction, request.getAction()); + assertNull(request.getRegion()); + assertTrue(request.getArguments().isEmpty()); + + final String expectedRegion = "us-east-1"; + final Document expectedArgs = new Document("hi", "hello"); + final AwsRequest fullRequest = new AwsRequest.Builder() + .withService(expectedService) + .withAction(expectedAction) + .withRegion(expectedRegion) + .withArguments(expectedArgs) + .build(); + assertEquals(expectedService, fullRequest.getService()); + assertEquals(expectedAction, fullRequest.getAction()); + assertEquals(expectedRegion, fullRequest.getRegion()); + assertEquals(expectedArgs, fullRequest.getArguments()); + } +} diff --git a/core/services/aws/src/test/java/com/mongodb/stitch/core/services/aws/internal/CoreAwsServiceClientUnitTests.java b/core/services/aws/src/test/java/com/mongodb/stitch/core/services/aws/internal/CoreAwsServiceClientUnitTests.java new file mode 100644 index 000000000..ed76b37c4 --- /dev/null +++ b/core/services/aws/src/test/java/com/mongodb/stitch/core/services/aws/internal/CoreAwsServiceClientUnitTests.java @@ -0,0 +1,119 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.stitch.core.services.http.aws; + +import static com.mongodb.stitch.core.testutils.Assert.assertThrows; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import com.mongodb.stitch.core.services.aws.AwsRequest; +import com.mongodb.stitch.core.services.aws.internal.CoreAwsServiceClient; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; +import java.util.List; +import org.bson.Document; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +public class CoreAwsServiceClientUnitTests { + + @Test + @SuppressWarnings("unchecked") + public void testExecute() { + final CoreStitchServiceClient service = Mockito.mock(CoreStitchServiceClient.class); + final CoreAwsServiceClient client = new CoreAwsServiceClient(service); + + final String expectedService = "ses"; + final String expectedAction = "send"; + final String expectedRegion = "us-east-1"; + final Document expectedArguments = new Document("hi", "hello"); + + final AwsRequest request = new AwsRequest.Builder() + .withService(expectedService) + .withAction(expectedAction) + .withRegion(expectedRegion) + .withArguments(expectedArguments) + .build(); + + final Document response = new Document("email", "sent"); + + doReturn(response) + .when(service).callFunction(any(), any(), any(Class.class)); + + final Document result = client.execute(request, Document.class); + assertEquals(result, response); + + final ArgumentCaptor funcNameArg = ArgumentCaptor.forClass(String.class); + final ArgumentCaptor funcArgsArg = ArgumentCaptor.forClass(List.class); + final ArgumentCaptor resultClassArg = + ArgumentCaptor.forClass(Class.class); + verify(service) + .callFunction( + funcNameArg.capture(), + funcArgsArg.capture(), + resultClassArg.capture()); + + assertEquals("execute", funcNameArg.getValue()); + assertEquals(1, funcArgsArg.getValue().size()); + final Document expectedArgs = new Document(); + expectedArgs.put("aws_service", expectedService); + expectedArgs.put("aws_action", expectedAction); + expectedArgs.put("aws_region", expectedRegion); + expectedArgs.put("aws_arguments", expectedArguments); + assertEquals(expectedArgs, funcArgsArg.getValue().get(0)); + assertEquals(Document.class, resultClassArg.getValue()); + + verify(service) + .callFunction( + funcNameArg.capture(), + funcArgsArg.capture(), + resultClassArg.capture()); + + final AwsRequest request2 = new AwsRequest.Builder() + .withService(expectedService) + .withAction(expectedAction) + .build(); + + final Document result2 = client.execute(request2, Document.class); + assertEquals(result2, response); + + verify(service, times(2)) + .callFunction( + funcNameArg.capture(), + funcArgsArg.capture(), + resultClassArg.capture()); + + assertEquals("execute", funcNameArg.getValue()); + assertEquals(1, funcArgsArg.getValue().size()); + final Document expectedArgs2 = new Document(); + expectedArgs2.put("aws_service", expectedService); + expectedArgs2.put("aws_action", expectedAction); + expectedArgs2.put("aws_arguments", new Document()); + assertEquals(expectedArgs2, funcArgsArg.getValue().get(0)); + assertEquals(Document.class, resultClassArg.getValue()); + + // Should pass along errors + doThrow(new IllegalArgumentException("whoops")) + .when(service).callFunction(any(), any(), any(Class.class)); + assertThrows(() -> client.execute(request, Document.class), + IllegalArgumentException.class); + } +} diff --git a/core/services/fcm/src/main/java/com/mongodb/stitch/core/services/fcm/internal/CoreFcmServiceClient.java b/core/services/fcm/src/main/java/com/mongodb/stitch/core/services/fcm/internal/CoreFcmServiceClient.java index 31414f7fc..0fc2b90de 100644 --- a/core/services/fcm/src/main/java/com/mongodb/stitch/core/services/fcm/internal/CoreFcmServiceClient.java +++ b/core/services/fcm/src/main/java/com/mongodb/stitch/core/services/fcm/internal/CoreFcmServiceClient.java @@ -38,7 +38,7 @@ public FcmSendMessageResult sendMessageTo( ) { final Document args = getSendMessageRequest(request); args.put(SendFields.TO_FIELD, to); - return service.callFunctionInternal( + return service.callFunction( SEND_ACTION, Collections.singletonList(args), ResultDecoders.sendMessageResponseDecoder); } @@ -48,7 +48,7 @@ public FcmSendMessageResult sendMessageToUsers( ) { final Document args = getSendMessageRequest(request); args.put(SendFields.USER_IDS_FIELD, userIds); - return service.callFunctionInternal( + return service.callFunction( SEND_ACTION, Collections.singletonList(args), ResultDecoders.sendMessageResponseDecoder); } @@ -58,7 +58,7 @@ public FcmSendMessageResult sendMessageToRegistrationTokens( ) { final Document args = getSendMessageRequest(request); args.put(SendFields.REGISTRATION_TOKENS_FIELD, registrationTokens); - return service.callFunctionInternal( + return service.callFunction( SEND_ACTION, Collections.singletonList(args), ResultDecoders.sendMessageResponseDecoder); } diff --git a/core/services/fcm/src/test/java/com/mongodb/stitch/core/services/fcm/internal/CoreFcmServiceClientUnitTests.java b/core/services/fcm/src/test/java/com/mongodb/stitch/core/services/fcm/internal/CoreFcmServiceClientUnitTests.java index 701f629b3..f3966e5c3 100644 --- a/core/services/fcm/src/test/java/com/mongodb/stitch/core/services/fcm/internal/CoreFcmServiceClientUnitTests.java +++ b/core/services/fcm/src/test/java/com/mongodb/stitch/core/services/fcm/internal/CoreFcmServiceClientUnitTests.java @@ -103,7 +103,7 @@ public void testSendMessage() { failureDetails); doReturn(result) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final String to = "who"; assertEquals(result, client.sendMessageTo(to, fullRequest)); @@ -113,7 +113,7 @@ public void testSendMessage() { final ArgumentCaptor> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -151,7 +151,7 @@ public void testSendMessage() { assertEquals(result, client.sendMessageToRegistrationTokens(registrationTokens, fullRequest)); verify(service, times(2)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -168,7 +168,7 @@ public void testSendMessage() { assertEquals(result, client.sendMessageToUsers(userIds, fullRequest)); verify(service, times(3)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -183,7 +183,7 @@ public void testSendMessage() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> { client.sendMessageTo( "who", diff --git a/core/services/http/src/main/java/com/mongodb/stitch/core/services/http/internal/CoreHttpServiceClient.java b/core/services/http/src/main/java/com/mongodb/stitch/core/services/http/internal/CoreHttpServiceClient.java index 1a21f0356..e27c39d5a 100644 --- a/core/services/http/src/main/java/com/mongodb/stitch/core/services/http/internal/CoreHttpServiceClient.java +++ b/core/services/http/src/main/java/com/mongodb/stitch/core/services/http/internal/CoreHttpServiceClient.java @@ -82,7 +82,7 @@ public HttpResponse execute(final HttpRequest request) { args.put(RequestAction.HTTP_FOLLOW_REDIRECTS_PARAM, request.getFollowRedirects()); } - return service.callFunctionInternal( + return service.callFunction( action, Collections.singletonList(args), ResultDecoders.httpResponseDecoder); diff --git a/core/services/http/src/test/java/com/mongodb/stitch/core/services/http/internal/CoreHttpServiceClientUnitTests.java b/core/services/http/src/test/java/com/mongodb/stitch/core/services/http/internal/CoreHttpServiceClientUnitTests.java index b4c20da15..41b3495d7 100644 --- a/core/services/http/src/test/java/com/mongodb/stitch/core/services/http/internal/CoreHttpServiceClientUnitTests.java +++ b/core/services/http/src/test/java/com/mongodb/stitch/core/services/http/internal/CoreHttpServiceClientUnitTests.java @@ -78,7 +78,7 @@ public void testExecute() { "body".getBytes(StandardCharsets.UTF_8)); doReturn(response) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final HttpResponse result = client.execute(request); assertEquals(result, response); @@ -88,7 +88,7 @@ public void testExecute() { final ArgumentCaptor> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -109,7 +109,7 @@ public void testExecute() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> client.execute(request), IllegalArgumentException.class); } diff --git a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/AggregateOperation.java b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/AggregateOperation.java index ce7fc526b..91006f5de 100644 --- a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/AggregateOperation.java +++ b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/AggregateOperation.java @@ -61,7 +61,7 @@ public Collection execute(final CoreStitchServiceClient service) { args.put("collection", namespace.getCollectionName()); args.put("pipeline", pipeline); - return service.callFunctionInternal( + return service.callFunction( "aggregate", Collections.singletonList(args), new CollectionDecoder<>(decoder)); diff --git a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/CountOperation.java b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/CountOperation.java index 4e7b270a7..0ef1bfae0 100644 --- a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/CountOperation.java +++ b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/CountOperation.java @@ -50,7 +50,7 @@ public Long execute(final CoreStitchServiceClient service) { args.put("query", filter); args.put("limit", limit); - return service.callFunctionInternal( + return service.callFunction( "count", Collections.singletonList(args), Long.class); diff --git a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/DeleteManyOperation.java b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/DeleteManyOperation.java index b262a42a8..12372c298 100644 --- a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/DeleteManyOperation.java +++ b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/DeleteManyOperation.java @@ -43,7 +43,7 @@ public RemoteDeleteResult execute(final CoreStitchServiceClient service) { args.put("collection", namespace.getCollectionName()); args.put("query", filter); - return service.callFunctionInternal( + return service.callFunction( "deleteMany", Collections.singletonList(args), ResultDecoders.deleteResultDecoder); diff --git a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/DeleteOneOperation.java b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/DeleteOneOperation.java index c5b143381..b758374a0 100644 --- a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/DeleteOneOperation.java +++ b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/DeleteOneOperation.java @@ -42,7 +42,7 @@ public RemoteDeleteResult execute(final CoreStitchServiceClient service) { args.put("collection", namespace.getCollectionName()); args.put("query", filter); - return service.callFunctionInternal( + return service.callFunction( "deleteOne", Collections.singletonList(args), ResultDecoders.deleteResultDecoder); diff --git a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/FindOperation.java b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/FindOperation.java index b8ca34088..574ec1006 100644 --- a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/FindOperation.java +++ b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/FindOperation.java @@ -104,7 +104,7 @@ public Collection execute(final CoreStitchServiceClient service) { args.put("project", projection); args.put("sort", sort); - return service.callFunctionInternal( + return service.callFunction( "find", Collections.singletonList(args), new CollectionDecoder<>(decoder)); diff --git a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/InsertManyOperation.java b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/InsertManyOperation.java index 2e88a5887..d312bd5c0 100644 --- a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/InsertManyOperation.java +++ b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/InsertManyOperation.java @@ -42,7 +42,7 @@ public RemoteInsertManyResult execute(final CoreStitchServiceClient service) { args.put("collection", namespace.getCollectionName()); args.put("documents", documents); - return service.callFunctionInternal( + return service.callFunction( "insertMany", Collections.singletonList(args), ResultDecoders.insertManyResultDecoder); diff --git a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/InsertOneOperation.java b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/InsertOneOperation.java index b8a777897..de1be7403 100644 --- a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/InsertOneOperation.java +++ b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/InsertOneOperation.java @@ -41,7 +41,7 @@ public RemoteInsertOneResult execute(final CoreStitchServiceClient service) { args.put("collection", namespace.getCollectionName()); args.put("document", document); - return service.callFunctionInternal( + return service.callFunction( "insertOne", Collections.singletonList(args), ResultDecoders.insertOneResultDecoder); diff --git a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/UpdateManyOperation.java b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/UpdateManyOperation.java index 91c7c9e60..e60ab70db 100644 --- a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/UpdateManyOperation.java +++ b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/UpdateManyOperation.java @@ -53,7 +53,7 @@ public RemoteUpdateResult execute(final CoreStitchServiceClient service) { args.put("update", update); args.put("upsert", upsert); - return service.callFunctionInternal( + return service.callFunction( "updateMany", Collections.singletonList(args), ResultDecoders.updateResultDecoder); diff --git a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/UpdateOneOperation.java b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/UpdateOneOperation.java index 9ae544d94..65b088afc 100644 --- a/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/UpdateOneOperation.java +++ b/core/services/mongodb-remote/src/main/java/com/mongodb/stitch/core/services/mongodb/remote/internal/UpdateOneOperation.java @@ -53,7 +53,7 @@ public RemoteUpdateResult execute(final CoreStitchServiceClient service) { args.put("update", update); args.put("upsert", upsert); - return service.callFunctionInternal( + return service.callFunction( "updateOne", Collections.singletonList(args), ResultDecoders.updateResultDecoder); diff --git a/core/services/mongodb-remote/src/test/java/com/mongodb/stitch/core/services/mongodb/remote/internal/CoreRemoteMongoCollectionUnitTests.java b/core/services/mongodb-remote/src/test/java/com/mongodb/stitch/core/services/mongodb/remote/internal/CoreRemoteMongoCollectionUnitTests.java index e3327ccab..114609cef 100644 --- a/core/services/mongodb-remote/src/test/java/com/mongodb/stitch/core/services/mongodb/remote/internal/CoreRemoteMongoCollectionUnitTests.java +++ b/core/services/mongodb-remote/src/test/java/com/mongodb/stitch/core/services/mongodb/remote/internal/CoreRemoteMongoCollectionUnitTests.java @@ -109,7 +109,7 @@ public void testCount() { final CoreRemoteMongoCollection coll = getCollection(client); doReturn(42L) - .when(service).callFunctionInternal(any(), any(), any(Class.class)); + .when(service).callFunction(any(), any(), any(Class.class)); assertEquals(42, coll.count()); final ArgumentCaptor funcNameArg = ArgumentCaptor.forClass(String.class); @@ -117,7 +117,7 @@ public void testCount() { final ArgumentCaptor> resultClassArg = ArgumentCaptor.forClass(Class.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -136,7 +136,7 @@ public void testCount() { assertEquals(42, coll.count(expectedFilter, new RemoteCountOptions().limit(5))); verify(service, times(2)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -152,7 +152,7 @@ public void testCount() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Class.class)); + .when(service).callFunction(any(), any(), any(Class.class)); assertThrows(coll::count, IllegalArgumentException.class); } @@ -169,7 +169,7 @@ public void testFind() { final Document doc2 = new Document("three", 4); final Collection docs = Arrays.asList(doc1, doc2); doReturn(docs) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final CoreRemoteFindIterable iter = coll.find(); assertEquals(docs, iter.into(new ArrayList<>())); @@ -179,7 +179,7 @@ public void testFind() { final ArgumentCaptor>> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -206,7 +206,7 @@ public void testFind() { assertEquals(docs, iter.into(new ArrayList<>())); verify(service, times(2)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -221,13 +221,13 @@ public void testFind() { assertEquals(CollectionDecoder.class, resultClassArg.getValue().getClass()); doReturn(Arrays.asList(1, 2, 3)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertEquals(Arrays.asList(1, 2, 3), coll.find(expectedFilter, Integer.class).into(new ArrayList<>())); // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> coll.find().first(), IllegalArgumentException.class); } @@ -244,7 +244,7 @@ public void testAggregate() { final Document doc2 = new Document("three", 4); final Collection docs = Arrays.asList(doc1, doc2); doReturn(docs) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); CoreRemoteAggregateIterable iter = coll.aggregate(Collections.emptyList()); assertEquals(docs, iter.into(new ArrayList<>())); @@ -254,7 +254,7 @@ public void testAggregate() { final ArgumentCaptor>> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -275,7 +275,7 @@ public void testAggregate() { assertEquals(docs, iter.into(new ArrayList<>())); verify(service, times(2)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -288,7 +288,7 @@ public void testAggregate() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> coll.aggregate(Collections.emptyList()).first(), IllegalArgumentException.class); } @@ -304,7 +304,7 @@ public void testInsertOne() { final Document doc1 = new Document("one", 2); final BsonObjectId id = new BsonObjectId(); doReturn(new RemoteInsertOneResult(id)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final RemoteInsertOneResult result = coll.insertOne(doc1); assertEquals(id, result.getInsertedId()); @@ -315,7 +315,7 @@ public void testInsertOne() { final ArgumentCaptor>> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -331,7 +331,7 @@ public void testInsertOne() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> coll.insertOne(new Document()), IllegalArgumentException.class); } @@ -353,7 +353,7 @@ public void testInsertMany() { ids.put(0L, id1); ids.put(1L, id2); doReturn(new RemoteInsertManyResult(ids)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final RemoteInsertManyResult result = coll.insertMany(Arrays.asList(doc1, doc2)); assertEquals(ids, result.getInsertedIds()); @@ -365,7 +365,7 @@ public void testInsertMany() { final ArgumentCaptor>> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -384,7 +384,7 @@ public void testInsertMany() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> coll.insertMany(Collections.singletonList(new Document())), IllegalArgumentException.class); } @@ -398,7 +398,7 @@ public void testDeleteOne() { final CoreRemoteMongoCollection coll = getCollection(client); doReturn(new RemoteDeleteResult(1)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final Document expectedFilter = new Document("one", 2); final RemoteDeleteResult result = coll.deleteOne(expectedFilter); @@ -409,7 +409,7 @@ public void testDeleteOne() { final ArgumentCaptor>> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -426,7 +426,7 @@ public void testDeleteOne() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> coll.deleteOne(new Document()), IllegalArgumentException.class); } @@ -440,7 +440,7 @@ public void testDeleteMany() { final CoreRemoteMongoCollection coll = getCollection(client); doReturn(new RemoteDeleteResult(1)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final Document expectedFilter = new Document("one", 2); final RemoteDeleteResult result = coll.deleteMany(expectedFilter); @@ -451,7 +451,7 @@ public void testDeleteMany() { final ArgumentCaptor>> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -468,7 +468,7 @@ public void testDeleteMany() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> coll.deleteMany(new Document()), IllegalArgumentException.class); } @@ -483,7 +483,7 @@ public void testUpdateOne() { final BsonObjectId id = new BsonObjectId(); doReturn(new RemoteUpdateResult(1, 1, id)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final Document expectedFilter = new Document("one", 2); final Document expectedUpdate = new Document("three", 4); @@ -497,7 +497,7 @@ public void testUpdateOne() { final ArgumentCaptor>> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -520,7 +520,7 @@ public void testUpdateOne() { assertEquals(1, result.getModifiedCount()); assertEquals(id, result.getUpsertedId()); verify(service, times(2)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -533,7 +533,7 @@ public void testUpdateOne() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> coll.updateOne(new Document(), new Document()), IllegalArgumentException.class); } @@ -548,7 +548,7 @@ public void testUpdateMany() { final BsonObjectId id = new BsonObjectId(); doReturn(new RemoteUpdateResult(1, 1, id)) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); final Document expectedFilter = new Document("one", 2); final Document expectedUpdate = new Document("three", 4); @@ -562,7 +562,7 @@ public void testUpdateMany() { final ArgumentCaptor>> resultClassArg = ArgumentCaptor.forClass(Decoder.class); verify(service) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -586,7 +586,7 @@ public void testUpdateMany() { assertEquals(1, result.getModifiedCount()); assertEquals(id, result.getUpsertedId()); verify(service, times(2)) - .callFunctionInternal( + .callFunction( funcNameArg.capture(), funcArgsArg.capture(), resultClassArg.capture()); @@ -599,7 +599,7 @@ public void testUpdateMany() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any(), any(Decoder.class)); + .when(service).callFunction(any(), any(), any(Decoder.class)); assertThrows(() -> coll.updateMany(new Document(), new Document()), IllegalArgumentException.class); } diff --git a/core/services/twilio/src/main/java/com/mongodb/stitch/core/services/twilio/internal/CoreTwilioServiceClient.java b/core/services/twilio/src/main/java/com/mongodb/stitch/core/services/twilio/internal/CoreTwilioServiceClient.java index 640e23b1c..21fcdfeda 100644 --- a/core/services/twilio/src/main/java/com/mongodb/stitch/core/services/twilio/internal/CoreTwilioServiceClient.java +++ b/core/services/twilio/src/main/java/com/mongodb/stitch/core/services/twilio/internal/CoreTwilioServiceClient.java @@ -41,7 +41,7 @@ public void sendMessage( if (mediaUrl != null) { args.put("mediaUrl", mediaUrl); } - service.callFunctionInternal("send", Collections.singletonList(args)); + service.callFunction("send", Collections.singletonList(args)); } public void sendMessage(final String to, final String from, final String body) { diff --git a/core/services/twilio/src/test/java/com/mongodb/stitch/core/services/twilio/internal/CoreTwilioServiceClientUnitTests.java b/core/services/twilio/src/test/java/com/mongodb/stitch/core/services/twilio/internal/CoreTwilioServiceClientUnitTests.java index 2596ce202..341c1ba2e 100644 --- a/core/services/twilio/src/test/java/com/mongodb/stitch/core/services/twilio/internal/CoreTwilioServiceClientUnitTests.java +++ b/core/services/twilio/src/test/java/com/mongodb/stitch/core/services/twilio/internal/CoreTwilioServiceClientUnitTests.java @@ -45,7 +45,7 @@ public void testSendMessage() { final ArgumentCaptor funcNameArg = ArgumentCaptor.forClass(String.class); final ArgumentCaptor funcArgsArg = ArgumentCaptor.forClass(List.class); - verify(service).callFunctionInternal(funcNameArg.capture(), funcArgsArg.capture()); + verify(service).callFunction(funcNameArg.capture(), funcArgsArg.capture()); assertEquals("send", funcNameArg.getValue()); assertEquals(1, funcArgsArg.getValue().size()); @@ -57,7 +57,7 @@ public void testSendMessage() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any()); + .when(service).callFunction(any(), any()); assertThrows(() -> { client.sendMessage(to, from, body); return null; @@ -78,7 +78,7 @@ public void testSendMessageWithMedia() { final ArgumentCaptor funcNameArg = ArgumentCaptor.forClass(String.class); final ArgumentCaptor funcArgsArg = ArgumentCaptor.forClass(List.class); - verify(service).callFunctionInternal(funcNameArg.capture(), funcArgsArg.capture()); + verify(service).callFunction(funcNameArg.capture(), funcArgsArg.capture()); assertEquals("send", funcNameArg.getValue()); assertEquals(1, funcArgsArg.getValue().size()); @@ -91,7 +91,7 @@ public void testSendMessageWithMedia() { // Should pass along errors doThrow(new IllegalArgumentException("whoops")) - .when(service).callFunctionInternal(any(), any()); + .when(service).callFunction(any(), any()); assertThrows(() -> { client.sendMessage(to, from, body); return null; diff --git a/server/core/src/main/java/com/mongodb/stitch/server/core/StitchAppClient.java b/server/core/src/main/java/com/mongodb/stitch/server/core/StitchAppClient.java index 0415808e0..b956578cd 100644 --- a/server/core/src/main/java/com/mongodb/stitch/server/core/StitchAppClient.java +++ b/server/core/src/main/java/com/mongodb/stitch/server/core/StitchAppClient.java @@ -17,9 +17,9 @@ package com.mongodb.stitch.server.core; import com.mongodb.stitch.server.core.auth.StitchAuth; +import com.mongodb.stitch.server.core.services.StitchServiceClient; import com.mongodb.stitch.server.core.services.internal.NamedServiceClientFactory; import com.mongodb.stitch.server.core.services.internal.ServiceClientFactory; - import java.io.Closeable; import java.io.IOException; import java.util.List; @@ -61,6 +61,14 @@ public interface StitchAppClient extends Closeable { */ T getServiceClient(final ServiceClientFactory factory); + /** + * Gets a general purpose client for the given named service. + * + * @param serviceName the name of the service. + * @return a client to interact with the service. + */ + StitchServiceClient getServiceClient(final String serviceName); + /** * Calls the specified Stitch function. * diff --git a/server/core/src/main/java/com/mongodb/stitch/server/core/internal/StitchAppClientImpl.java b/server/core/src/main/java/com/mongodb/stitch/server/core/internal/StitchAppClientImpl.java index 96adcaca1..a06e3a251 100644 --- a/server/core/src/main/java/com/mongodb/stitch/server/core/internal/StitchAppClientImpl.java +++ b/server/core/src/main/java/com/mongodb/stitch/server/core/internal/StitchAppClientImpl.java @@ -21,13 +21,14 @@ import com.mongodb.stitch.core.internal.CoreStitchAppClient; import com.mongodb.stitch.core.internal.net.StitchAppRoutes; import com.mongodb.stitch.core.internal.net.StitchRequestClient; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClientImpl; import com.mongodb.stitch.server.core.StitchAppClient; import com.mongodb.stitch.server.core.auth.StitchAuth; import com.mongodb.stitch.server.core.auth.internal.StitchAuthImpl; +import com.mongodb.stitch.server.core.services.StitchServiceClient; import com.mongodb.stitch.server.core.services.internal.NamedServiceClientFactory; import com.mongodb.stitch.server.core.services.internal.ServiceClientFactory; import com.mongodb.stitch.server.core.services.internal.StitchServiceClientImpl; - import java.io.IOException; import java.util.List; import org.bson.codecs.Decoder; @@ -76,18 +77,18 @@ public StitchAuth getAuth() { public T getServiceClient( final NamedServiceClientFactory factory, final String serviceName) { return factory.getClient( - new StitchServiceClientImpl( - auth, - routes.getServiceRoutes(), - serviceName, - info.getCodecRegistry()), + new CoreStitchServiceClientImpl( + auth, + routes.getServiceRoutes(), + serviceName, + info.getCodecRegistry()), info); } @Override public T getServiceClient(final ServiceClientFactory factory) { return factory.getClient( - new StitchServiceClientImpl( + new CoreStitchServiceClientImpl( auth, routes.getServiceRoutes(), "", @@ -95,10 +96,20 @@ public T getServiceClient(final ServiceClientFactory factory) { info); } + @Override + public StitchServiceClient getServiceClient(final String serviceName) { + return new StitchServiceClientImpl( + new CoreStitchServiceClientImpl( + auth, + routes.getServiceRoutes(), + serviceName, + info.getCodecRegistry())); + } + @Override public void callFunction( final String name, final List args) { - coreClient.callFunctionInternal(name, args, null); + coreClient.callFunction(name, args, null); } @Override @@ -106,13 +117,13 @@ public void callFunction( final String name, final List args, final Long requestTimeout) { - coreClient.callFunctionInternal(name, args, requestTimeout); + coreClient.callFunction(name, args, requestTimeout); } @Override public ResultT callFunction( final String name, final List args, final Class resultClass) { - return coreClient.callFunctionInternal(name, args, null, resultClass); + return coreClient.callFunction(name, args, null, resultClass); } @Override @@ -121,7 +132,7 @@ public ResultT callFunction( final List args, final Long requestTimeout, final Class resultClass) { - return coreClient.callFunctionInternal(name, args, requestTimeout, resultClass); + return coreClient.callFunction(name, args, requestTimeout, resultClass); } @Override @@ -131,7 +142,7 @@ public ResultT callFunction( final Class resultClass, final CodecRegistry codecRegistry ) { - return coreClient.callFunctionInternal(name, args, null, resultClass, codecRegistry); + return coreClient.callFunction(name, args, null, resultClass, codecRegistry); } @Override @@ -142,13 +153,13 @@ public ResultT callFunction( final Class resultClass, final CodecRegistry codecRegistry ) { - return coreClient.callFunctionInternal(name, args, requestTimeout, resultClass, codecRegistry); + return coreClient.callFunction(name, args, requestTimeout, resultClass, codecRegistry); } @Override public ResultT callFunction( final String name, final List args, final Decoder resultDecoder) { - return coreClient.callFunctionInternal(name, args, null, resultDecoder); + return coreClient.callFunction(name, args, null, resultDecoder); } @Override @@ -157,7 +168,7 @@ public ResultT callFunction( final List args, final Long requestTimeout, final Decoder resultDecoder) { - return coreClient.callFunctionInternal(name, args, requestTimeout, resultDecoder); + return coreClient.callFunction(name, args, requestTimeout, resultDecoder); } /** diff --git a/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/StitchServiceClient.java b/server/core/src/main/java/com/mongodb/stitch/server/core/services/StitchServiceClient.java similarity index 52% rename from server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/StitchServiceClient.java rename to server/core/src/main/java/com/mongodb/stitch/server/core/services/StitchServiceClient.java index 6df8109c9..62cf097f0 100644 --- a/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/StitchServiceClient.java +++ b/server/core/src/main/java/com/mongodb/stitch/server/core/services/StitchServiceClient.java @@ -14,14 +14,41 @@ * limitations under the License. */ -package com.mongodb.stitch.server.core.services.internal; - -import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; +package com.mongodb.stitch.server.core.services; import java.util.List; import org.bson.codecs.Decoder; +import org.bson.codecs.configuration.CodecRegistry; + +/** + * StitchServiceClient acts as a general purpose client for working with services that either + * not defined or well defined by this SDK. It functions similarly to + * {@link com.mongodb.stitch.server.core.StitchAppClient#callFunction}. + */ +public interface StitchServiceClient { + + /** + * Calls the specified Stitch service function. + * + * @param name the name of the Stitch service function to call. + * @param args the arguments to pass to the function. + */ + void callFunction( + final String name, final List args); + + /** + * Calls the specified Stitch service function, and decodes the response into a value using the + * provided {@link Decoder}. + * + * @param name the name of the Stitch service function to call. + * @param args the arguments to pass to the function. + * @param resultDecoder the {@link Decoder} to use to decode the response into a value. + * @param the type into which the response will be decoded. + * @return the decoded value. + */ + ResultT callFunction( + final String name, final List args, final Decoder resultDecoder); -public interface StitchServiceClient extends CoreStitchServiceClient { /** * Calls the specified Stitch service function, and decodes the response into an instance of the * specified type. The response will be decoded using the codec registry specified when the app @@ -40,10 +67,23 @@ ResultT callFunction( /** * Calls the specified Stitch service function, and decodes the response into an instance of the - * specified type. The response will be decoded using the codec registry specified when the app - * client was configured. If no codec registry was configured, a default codec registry will be - * used. The default codec registry supports the mappings specified here + * specified type. The response will be decoded using the codec registry given. + * + * @param name the name of the Stitch service function to call. + * @param args the arguments to pass to the function. + * @param resultClass the class that the response should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return the decoded value. + */ + ResultT callFunction( + final String name, + final List args, + final Class resultClass, + final CodecRegistry codecRegistry); + + /** + * Calls the specified Stitch service function. * Also accepts a timeout in milliseconds. Use this for functions that may run longer than the * client-wide default timeout (15 seconds by default). * @@ -51,7 +91,22 @@ ResultT callFunction( * @param args the arguments to pass to the function. * @param requestTimeout the number of milliseconds the client should wait for a response from the * server before failing with an error. - * @param resultClass the class that the response should be decoded as. + */ + void callFunction( + final String name, + final List args, + final Long requestTimeout); + + /** + * Calls the specified Stitch service function, and decodes the response into a value using the + * provided {@link Decoder}. Also accepts a timeout in milliseconds. Use this for functions that + * may run longer than the client-wide default timeout (15 seconds by default). + * + * @param name the name of the Stitch service function to call. + * @param args the arguments to pass to the function. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultDecoder the {@link Decoder} to use to decode the response into a value. * @param the type into which the response will be decoded. * @return the decoded value. */ @@ -59,37 +114,66 @@ ResultT callFunction( final String name, final List args, final Long requestTimeout, - final Class resultClass); + final Decoder resultDecoder); /** - * Calls the specified Stitch service function, and decodes the response into a value using the - * provided {@link Decoder}. + * Calls the specified Stitch service function, and decodes the response into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * Also accepts a timeout in milliseconds. Use this for functions that may run longer than the + * client-wide default timeout (15 seconds by default). * * @param name the name of the Stitch service function to call. * @param args the arguments to pass to the function. - * @param resultDecoder the {@link Decoder} to use to decode the response into a value. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the response should be decoded as. * @param the type into which the response will be decoded. * @return the decoded value. */ ResultT callFunction( - final String name, final List args, final Decoder resultDecoder); + final String name, + final List args, + final Long requestTimeout, + final Class resultClass); /** - * Calls the specified Stitch service function, and decodes the response into a value using the - * provided {@link Decoder}. Also accepts a timeout in milliseconds. Use this for functions that - * may run longer than the client-wide default timeout (15 seconds by default). + * Calls the specified Stitch service function, and decodes the response into an instance of the + * specified type. The response will be decoded using the codec registry given. + * Also accepts a timeout in milliseconds. Use this for functions that may run longer than the + * client-wide default timeout (15 seconds by default). * * @param name the name of the Stitch service function to call. * @param args the arguments to pass to the function. * @param requestTimeout the number of milliseconds the client should wait for a response from the * server before failing with an error. - * @param resultDecoder the {@link Decoder} to use to decode the response into a value. + * @param resultClass the class that the response should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. * @param the type into which the response will be decoded. * @return the decoded value. */ ResultT callFunction( - final String name, - final List args, - final Long requestTimeout, - final Decoder resultDecoder); + final String name, + final List args, + final Long requestTimeout, + final Class resultClass, + final CodecRegistry codecRegistry); + + /** + * Get the codec registry that will be used to decode responses when a codec registry. + * + * @return the {@link CodecRegistry} + */ + CodecRegistry getCodecRegistry(); + + + /** + * Create a new StitchServiceClient instance with a different codec registry. + * + * @param codecRegistry the new {@link CodecRegistry} for the service client. + * @return a new StitchServiceClient instance with the different codec registry + */ + StitchServiceClient withCodecRegistry(final CodecRegistry codecRegistry); } diff --git a/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/NamedServiceClientFactory.java b/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/NamedServiceClientFactory.java index 12a9d7e15..a191f24a6 100644 --- a/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/NamedServiceClientFactory.java +++ b/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/NamedServiceClientFactory.java @@ -17,7 +17,10 @@ package com.mongodb.stitch.server.core.services.internal; import com.mongodb.stitch.core.StitchAppClientInfo; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; public interface NamedServiceClientFactory { - T getClient(final StitchServiceClient serviceClient, final StitchAppClientInfo appInfo); + T getClient( + final CoreStitchServiceClient serviceClient, + final StitchAppClientInfo appInfo); } diff --git a/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/ServiceClientFactory.java b/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/ServiceClientFactory.java index 170964f2c..795301dae 100644 --- a/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/ServiceClientFactory.java +++ b/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/ServiceClientFactory.java @@ -17,7 +17,10 @@ package com.mongodb.stitch.server.core.services.internal; import com.mongodb.stitch.core.StitchAppClientInfo; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; public interface ServiceClientFactory { - T getClient(final StitchServiceClient serviceClient, final StitchAppClientInfo appInfo); + T getClient( + final CoreStitchServiceClient serviceClient, + final StitchAppClientInfo appInfo); } diff --git a/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/StitchServiceClientImpl.java b/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/StitchServiceClientImpl.java index 2c05b49db..1027f8ead 100644 --- a/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/StitchServiceClientImpl.java +++ b/server/core/src/main/java/com/mongodb/stitch/server/core/services/internal/StitchServiceClientImpl.java @@ -16,30 +16,53 @@ package com.mongodb.stitch.server.core.services.internal; -import com.mongodb.stitch.core.auth.internal.StitchAuthRequestClient; -import com.mongodb.stitch.core.services.internal.CoreStitchServiceClientImpl; -import com.mongodb.stitch.core.services.internal.StitchServiceRoutes; +import com.mongodb.stitch.core.services.internal.CoreStitchServiceClient; +import com.mongodb.stitch.server.core.services.StitchServiceClient; import java.util.List; import org.bson.codecs.Decoder; import org.bson.codecs.configuration.CodecRegistry; -public final class StitchServiceClientImpl - extends CoreStitchServiceClientImpl implements StitchServiceClient { +public final class StitchServiceClientImpl implements StitchServiceClient { - public StitchServiceClientImpl( - final StitchAuthRequestClient requestClient, - final StitchServiceRoutes routes, - final String name, - final CodecRegistry codecRegistry - ) { - super(requestClient, routes, name, codecRegistry); + private final CoreStitchServiceClient proxy; + + public StitchServiceClientImpl(final CoreStitchServiceClient proxy) { + this.proxy = proxy; + } + + @Override + public void callFunction(final String name, final List args) { + proxy.callFunction(name, args); + } + + @Override + public ResultT callFunction( + final String name, final List args, final Decoder resultDecoder) { + return proxy.callFunction(name, args, null, resultDecoder); } @Override public ResultT callFunction( final String name, final List args, final Class resultClass) { - return callFunctionInternal(name, args, null, resultClass); + return proxy.callFunction(name, args, null, resultClass); + } + + @Override + public ResultT callFunction( + final String name, + final List args, + final Class resultClass, + final CodecRegistry codecRegistry) { + return proxy.callFunction(name, args, null, resultClass, codecRegistry); + } + + @Override + public void callFunction( + final String name, + final List args, + final Long requestTimeout) { + proxy.callFunction(name, args, requestTimeout); } @Override @@ -48,13 +71,16 @@ public ResultT callFunction( final List args, final Long requestTimeout, final Class resultClass) { - return callFunctionInternal(name, args, requestTimeout, resultClass); + return proxy.callFunction(name, args, requestTimeout, resultClass); } @Override public ResultT callFunction( - final String name, final List args, final Decoder resultDecoder) { - return callFunctionInternal(name, args, null, resultDecoder); + final String name, + final List args, + final Long requestTimeout, + final Decoder resultDecoder) { + return proxy.callFunction(name, args, requestTimeout, resultDecoder); } @Override @@ -62,7 +88,23 @@ public ResultT callFunction( final String name, final List args, final Long requestTimeout, - final Decoder resultDecoder) { - return callFunctionInternal(name, args, requestTimeout, resultDecoder); + final Class resultClass, + final CodecRegistry codecRegistry) { + return proxy.callFunction( + name, + args, + requestTimeout, + resultClass, + codecRegistry); + } + + @Override + public CodecRegistry getCodecRegistry() { + return proxy.getCodecRegistry(); + } + + @Override + public StitchServiceClient withCodecRegistry(final CodecRegistry codecRegistry) { + return new StitchServiceClientImpl(proxy.withCodecRegistry(codecRegistry)); } } diff --git a/server/core/src/main/java/com/mongodb/stitch/server/core/services/package-info.java b/server/core/src/main/java/com/mongodb/stitch/server/core/services/package-info.java new file mode 100644 index 000000000..ff8a6b9eb --- /dev/null +++ b/server/core/src/main/java/com/mongodb/stitch/server/core/services/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This package contains the definitions of service specific concepts. + */ +package com.mongodb.stitch.server.core.services; diff --git a/server/coretest/src/test/java/com/mongodb/stitch/server/core/StitchAppClientIntTests.kt b/server/coretest/src/test/java/com/mongodb/stitch/server/core/StitchAppClientIntTests.kt index aeb25b267..a40607f8f 100644 --- a/server/coretest/src/test/java/com/mongodb/stitch/server/core/StitchAppClientIntTests.kt +++ b/server/coretest/src/test/java/com/mongodb/stitch/server/core/StitchAppClientIntTests.kt @@ -13,7 +13,6 @@ import com.mongodb.stitch.core.auth.providers.userpassword.UserPasswordAuthProvi import com.mongodb.stitch.core.auth.providers.userpassword.UserPasswordCredential import com.mongodb.stitch.server.core.auth.providers.userpassword.UserPasswordAuthProviderClient import com.mongodb.stitch.server.testutils.BaseStitchServerIntTest -import com.mongodb.stitch.server.testutils.callFunction import io.jsonwebtoken.Jwts import io.jsonwebtoken.SignatureAlgorithm import org.bson.Document @@ -182,8 +181,8 @@ class StitchAppClientIntTests : BaseStitchServerIntTest() { client.auth.loginWithCredential(AnonymousCredential()) - val resultDoc = client.callFunction( - "testFunction", Arrays.asList(42, "hello") + val resultDoc = client.callFunction( + "testFunction", Arrays.asList(42, "hello"), Document::class.java ) assertTrue(resultDoc.containsKey("intValue")) diff --git a/server/services/aws-s3/src/main/java/com/mongodb/stitch/server/services/aws/s3/AwsS3ServiceClient.java b/server/services/aws-s3/src/main/java/com/mongodb/stitch/server/services/aws/s3/AwsS3ServiceClient.java index 343e0a16b..e8894f8aa 100644 --- a/server/services/aws-s3/src/main/java/com/mongodb/stitch/server/services/aws/s3/AwsS3ServiceClient.java +++ b/server/services/aws-s3/src/main/java/com/mongodb/stitch/server/services/aws/s3/AwsS3ServiceClient.java @@ -28,7 +28,10 @@ /** * The AWS S3 service client. + * + * @deprecated use AwsServiceClient instead. */ +@Deprecated public interface AwsS3ServiceClient { /** diff --git a/server/services/aws-s3/src/main/java/com/mongodb/stitch/server/services/aws/s3/internal/AwsS3ServiceClientImpl.java b/server/services/aws-s3/src/main/java/com/mongodb/stitch/server/services/aws/s3/internal/AwsS3ServiceClientImpl.java index 15d6519b6..cb574af92 100644 --- a/server/services/aws-s3/src/main/java/com/mongodb/stitch/server/services/aws/s3/internal/AwsS3ServiceClientImpl.java +++ b/server/services/aws-s3/src/main/java/com/mongodb/stitch/server/services/aws/s3/internal/AwsS3ServiceClientImpl.java @@ -25,6 +25,10 @@ import javax.annotation.Nonnull; import org.bson.types.Binary; +/** + * @deprecated use AwsServiceClient instead. + */ +@Deprecated public final class AwsS3ServiceClientImpl implements AwsS3ServiceClient { private final CoreAwsS3ServiceClient proxy; diff --git a/server/services/aws-ses/src/main/java/com/mongodb/stitch/server/services/aws/ses/AwsSesServiceClient.java b/server/services/aws-ses/src/main/java/com/mongodb/stitch/server/services/aws/ses/AwsSesServiceClient.java index 2a510e84a..317ee62a7 100644 --- a/server/services/aws-ses/src/main/java/com/mongodb/stitch/server/services/aws/ses/AwsSesServiceClient.java +++ b/server/services/aws-ses/src/main/java/com/mongodb/stitch/server/services/aws/ses/AwsSesServiceClient.java @@ -24,7 +24,10 @@ /** * The AWS SES service client. + * + * @deprecated use AwsServiceClient instead. */ +@Deprecated public interface AwsSesServiceClient { /** diff --git a/server/services/aws-ses/src/main/java/com/mongodb/stitch/server/services/aws/ses/internal/AwsSesServiceClientImpl.java b/server/services/aws-ses/src/main/java/com/mongodb/stitch/server/services/aws/ses/internal/AwsSesServiceClientImpl.java index 042d79b32..e2e1e8b7b 100644 --- a/server/services/aws-ses/src/main/java/com/mongodb/stitch/server/services/aws/ses/internal/AwsSesServiceClientImpl.java +++ b/server/services/aws-ses/src/main/java/com/mongodb/stitch/server/services/aws/ses/internal/AwsSesServiceClientImpl.java @@ -21,6 +21,10 @@ import com.mongodb.stitch.server.services.aws.ses.AwsSesServiceClient; import javax.annotation.Nonnull; +/** + * @deprecated use AwsServiceClient instead. + */ +@Deprecated public final class AwsSesServiceClientImpl implements AwsSesServiceClient { private final CoreAwsSesServiceClient proxy; diff --git a/server/services/aws/build.gradle b/server/services/aws/build.gradle new file mode 100644 index 000000000..48aadc7ed --- /dev/null +++ b/server/services/aws/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'java-library' +apply plugin: 'kotlin' +apply plugin: 'com.jfrog.bintray' + +ext.pomDisplayName = "Server AWS Service" + +buildscript { + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}" + classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.0' + } +} + +test { + Properties properties = new Properties() + File file = project.rootProject.file('local.properties') + if (file.exists()) { + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + } + systemProperty "test.stitch.awsAccessKeyId", properties.getProperty("test.stitch.awsAccessKeyId", "") + systemProperty "test.stitch.awsSecretAccessKey", properties.getProperty("test.stitch.awsSecretAccessKey", "") +} + +dependencies { + implementation project(':server:stitch-server-core') + api project(':core:core-services:stitch-core-services-aws') + + testImplementation project(':server:stitch-server-testutils') + testImplementation 'junit:junit:4.12' + testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" +} + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/AwsServiceClient.java b/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/AwsServiceClient.java new file mode 100644 index 000000000..b9c8663ae --- /dev/null +++ b/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/AwsServiceClient.java @@ -0,0 +1,175 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.stitch.server.services.aws; + +import com.mongodb.stitch.core.services.aws.AwsRequest; +import com.mongodb.stitch.core.services.aws.internal.CoreAwsServiceClient; +import com.mongodb.stitch.server.core.services.internal.NamedServiceClientFactory; +import com.mongodb.stitch.server.services.aws.internal.AwsServiceClientImpl; +import javax.annotation.Nonnull; +import org.bson.codecs.Decoder; +import org.bson.codecs.configuration.CodecRegistry; + +/** + * The AWS service client. + */ +public interface AwsServiceClient { + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + */ + void execute(@Nonnull final AwsRequest request); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + ResultT execute( + @Nonnull final AwsRequest request, + @Nonnull final Decoder resultDecoder); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + ResultT execute( + @Nonnull final AwsRequest request, + @Nonnull final Class resultClass); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + ResultT execute( + @Nonnull final AwsRequest request, + @Nonnull final Class resultClass, + @Nonnull final CodecRegistry codecRegistry + ); + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + */ + void execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout + ); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + ResultT execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout, + @Nonnull final Decoder resultDecoder + ); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + ResultT execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout, + @Nonnull final Class resultClass + ); + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + ResultT execute( + @Nonnull final AwsRequest request, + @Nonnull final Long requestTimeout, + @Nonnull final Class resultClass, + @Nonnull final CodecRegistry codecRegistry + ); + + /** + * Get the codec registry that will be used to decode responses when a codec registry. + * + * @return the {@link CodecRegistry} + */ + CodecRegistry getCodecRegistry(); + + /** + * Create a new AwsServiceClient instance with a different codec registry. + * + * @param codecRegistry the new {@link CodecRegistry} for the client. + * @return a new AwsServiceClient instance with the different codec registry + */ + AwsServiceClient withCodecRegistry(@Nonnull final CodecRegistry codecRegistry); + + NamedServiceClientFactory factory = + (service, appInfo) -> new AwsServiceClientImpl(new CoreAwsServiceClient(service)); +} diff --git a/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/internal/AwsServiceClientImpl.java b/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/internal/AwsServiceClientImpl.java new file mode 100644 index 000000000..8254faacb --- /dev/null +++ b/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/internal/AwsServiceClientImpl.java @@ -0,0 +1,189 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mongodb.stitch.server.services.aws.internal; + +import com.mongodb.stitch.core.services.aws.AwsRequest; +import com.mongodb.stitch.core.services.aws.internal.CoreAwsServiceClient; +import com.mongodb.stitch.server.services.aws.AwsServiceClient; +import org.bson.codecs.Decoder; +import org.bson.codecs.configuration.CodecRegistry; + +public final class AwsServiceClientImpl implements AwsServiceClient { + + private final CoreAwsServiceClient proxy; + + public AwsServiceClientImpl(final CoreAwsServiceClient client) { + this.proxy = client; + } + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + */ + public void execute(final AwsRequest request) { + proxy.execute(request); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute(final AwsRequest request, final Decoder resultDecoder) { + return proxy.execute(request, resultDecoder); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute(final AwsRequest request, final Class resultClass) { + return proxy.execute(request, resultClass); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute( + final AwsRequest request, + final Class resultClass, + final CodecRegistry codecRegistry + ) { + return proxy.execute(request, resultClass, codecRegistry); + } + + /** + * Executes the AWS request. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + */ + public void execute( + final AwsRequest request, + final Long requestTimeout + ) { + proxy.execute(request, requestTimeout); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultDecoder the {@link Decoder} to use to decode the result into a value. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute( + final AwsRequest request, + final Long requestTimeout, + final Decoder resultDecoder + ) { + return proxy.execute(request, requestTimeout, resultDecoder); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry specified when the app + * client was configured. If no codec registry was configured, a default codec registry will be + * used. The default codec registry supports the mappings specified here + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute( + final AwsRequest request, + final Long requestTimeout, + final Class resultClass + ) { + return proxy.execute(request, requestTimeout, resultClass); + } + + /** + * Executes the AWS request, and decodes the result into an instance of the + * specified type. The response will be decoded using the codec registry given. + * + * @param request the AWS request to execute. + * @param requestTimeout the number of milliseconds the client should wait for a response from the + * server before failing with an error. + * @param resultClass the class that the result should be decoded as. + * @param codecRegistry the codec registry used for de/serialization of the function call. + * @param the type into which the response will be decoded. + * @return the decoded result value. + */ + public ResultT execute( + final AwsRequest request, + final Long requestTimeout, + final Class resultClass, + final CodecRegistry codecRegistry + ) { + return proxy.execute(request, requestTimeout, resultClass, codecRegistry); + } + + /** + * Get the codec registry that will be used to decode responses when a codec registry. + * + * @return the {@link CodecRegistry} + */ + public CodecRegistry getCodecRegistry() { + return proxy.getCodecRegistry(); + } + + /** + * Create a new AwsServiceClient instance with a different codec registry. + * + * @param codecRegistry the new {@link CodecRegistry} for the client. + * @return a new AwsServiceClient instance with the different codec registry + */ + public AwsServiceClient withCodecRegistry(final CodecRegistry codecRegistry) { + return new AwsServiceClientImpl(proxy.withCodecRegistry(codecRegistry)); + } +} diff --git a/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/internal/package-info.java b/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/internal/package-info.java new file mode 100644 index 000000000..f40ab1de1 --- /dev/null +++ b/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/internal/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This internal package contains the Server SDK's implementation of the AWS service client. + */ +package com.mongodb.stitch.server.services.aws.internal; diff --git a/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/package-info.java b/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/package-info.java new file mode 100644 index 000000000..7f7667ddd --- /dev/null +++ b/server/services/aws/src/main/java/com/mongodb/stitch/server/services/aws/package-info.java @@ -0,0 +1,18 @@ +/* + * Copyright 2018-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** This package contains the AWS service client. */ +package com.mongodb.stitch.server.services.aws; diff --git a/server/services/aws/src/test/java/com/mongodb/stitch/server/services/aws/AwsServiceClientIntTests.kt b/server/services/aws/src/test/java/com/mongodb/stitch/server/services/aws/AwsServiceClientIntTests.kt new file mode 100644 index 000000000..a29f3a97d --- /dev/null +++ b/server/services/aws/src/test/java/com/mongodb/stitch/server/services/aws/AwsServiceClientIntTests.kt @@ -0,0 +1,127 @@ +package com.mongodb.stitch.server.services.aws + +import com.mongodb.stitch.server.testutils.BaseStitchServerIntTest +import com.mongodb.stitch.core.StitchServiceErrorCode +import com.mongodb.stitch.core.StitchServiceException +import com.mongodb.stitch.core.admin.authProviders.ProviderConfigs +import com.mongodb.stitch.core.admin.services.ServiceConfigs +import com.mongodb.stitch.core.admin.services.rules.RuleCreator +import com.mongodb.stitch.core.auth.providers.anonymous.AnonymousCredential +import com.mongodb.stitch.core.internal.common.IoUtils.readAllToString +import com.mongodb.stitch.core.internal.net.Method +import com.mongodb.stitch.core.internal.net.OkHttpTransport +import com.mongodb.stitch.core.internal.net.Request +import com.mongodb.stitch.core.services.aws.AwsRequest +import org.bson.Document +import org.bson.types.Binary +import org.bson.types.ObjectId +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Assume.assumeTrue +import org.junit.Before +import org.junit.Test +import java.nio.charset.StandardCharsets + +class AwsServiceClientIntTests : BaseStitchServerIntTest() { + + private val awsAccessKeyIdProp = "test.stitch.awsAccessKeyId" + private val awsSecretAccessKeyProp = "test.stitch.awsSecretAccessKey" + + private fun getAwsAccessKeyId(): String { + return System.getProperty(awsAccessKeyIdProp, "") + } + + private fun getAwsSecretAccessKey(): String { + return System.getProperty(awsSecretAccessKeyProp, "") + } + + @Before + override fun setup() { + assumeTrue("no AWS Access Key Id in properties; skipping test", getAwsAccessKeyId().isNotEmpty()) + assumeTrue("no AWS Secret Access Key in properties; skipping test", getAwsSecretAccessKey().isNotEmpty()) + super.setup() + } + + @Test + fun testPutObject() { + val app = createApp() + addProvider(app.second, ProviderConfigs.Anon) + val svc = addService( + app.second, + "aws", + "aws1", + ServiceConfigs.Aws(getAwsAccessKeyId(), getAwsSecretAccessKey())) + addRule(svc.second, RuleCreator.Aws( + "default", + setOf("s3:PutObject"))) + + val client = getAppClient(app.first) + client.auth.loginWithCredential(AnonymousCredential()) + + val awsS3 = client.getServiceClient(AwsServiceClient.factory, "aws1") + + // Putting to an bad bucket should fail + val bucket = "notmystuff" + val key = ObjectId().toHexString() + val acl = "public-read" + val contentType = "plain/text" + val body = "hello again friend; did you miss me" + val args = Document() + args["Bucket"] = bucket + args["Key"] = key + args["ACL"] = acl + args["ContentType"] = contentType + args["Body"] = body + + try { + awsS3.execute(AwsRequest.Builder() + .withService("s3") + .withAction("PutObject") + .withArguments(args) + .build()) + fail() + } catch (ex: StitchServiceException) { + assertEquals(StitchServiceErrorCode.AWS_ERROR, ex.errorCode) + } + + // Putting with all good params for S3 should work + val bucketGood = "stitch-test-sdkfiles" + val transport = OkHttpTransport() + + args["Bucket"] = bucketGood + var result = awsS3.execute(AwsRequest.Builder() + .withService("s3") + .withAction("PutObject") + .withArguments(args) + .build(), + Document::class.java) + var location = "https://stitch-test-sdkfiles.s3.amazonaws.com/$key" + assertTrue(result.containsKey("ETag")) + + var httpResult = transport.roundTrip(Request.Builder() + .withMethod(Method.GET) + .withUrl(location) + .withTimeout(1500L) + .build()) + assertEquals(body, readAllToString(httpResult.body)) + + val bodyBin = Binary(body.toByteArray(StandardCharsets.UTF_8)) + args["Body"] = bodyBin + awsS3.execute(AwsRequest.Builder() + .withService("s3") + .withAction("PutObject") + .withArguments(args) + .build(), + Document::class.java) + assertTrue(result.containsKey("ETag")) + location = "https://stitch-test-sdkfiles.s3.amazonaws.com/$key" + + httpResult = transport.roundTrip(Request.Builder() + .withMethod(Method.GET) + .withUrl(location) + .withTimeout(1500L) + .build()) + assertEquals(body, readAllToString(httpResult.body)) + } +} diff --git a/server/services/http/src/test/java/com/mongodb/stitch/server/services/http/HttpServiceClientIntTests.kt b/server/services/http/src/test/java/com/mongodb/stitch/server/services/http/HttpServiceClientIntTests.kt index dfa96db37..54f4f867f 100644 --- a/server/services/http/src/test/java/com/mongodb/stitch/server/services/http/HttpServiceClientIntTests.kt +++ b/server/services/http/src/test/java/com/mongodb/stitch/server/services/http/HttpServiceClientIntTests.kt @@ -104,7 +104,7 @@ class HttpServiceClientIntTests : BaseStitchServerIntTest() { assertEquals("200 OK", response.status) assertEquals(200, response.statusCode) - assertTrue(response.contentLength in 300..400) + assertTrue(response.contentLength in 300..500) assertNotNull(response.body) val dataDoc = Document.parse(String(response.body!!)) assertTrue(Arrays.equals( diff --git a/server/testutils/src/main/java/com/mongodb/stitch/server/testutils/TestUtils.kt b/server/testutils/src/main/java/com/mongodb/stitch/server/testutils/TestUtils.kt deleted file mode 100644 index 4a961eadb..000000000 --- a/server/testutils/src/main/java/com/mongodb/stitch/server/testutils/TestUtils.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.mongodb.stitch.server.testutils - -import com.mongodb.stitch.server.core.StitchAppClient - -// StitchAppClient extensions for kotlin -inline fun StitchAppClient.callFunction(name: String, args: List): T { - return this.callFunction(name, args, T::class.java) -} diff --git a/settings.gradle b/settings.gradle index 8c99bcba7..b5dc75aa2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,6 +2,7 @@ rootProject.name = 'Stitch-SDK' include ':core:sdk', ':core:admin-client', + ':core:services:aws', ':core:services:aws-s3', ':core:services:aws-ses', ':core:services:fcm', @@ -15,6 +16,7 @@ include ':core:sdk', ':server:sdk', ':server:core', ':server:coretest', + ':server:services:aws', ':server:services:aws-s3', ':server:services:aws-ses', ':server:services:fcm', @@ -28,6 +30,7 @@ include ':core:sdk', ':android:sdk', ':android:core', ':android:coretest', + ':android:services:aws', ':android:services:aws-s3', ':android:services:aws-ses', ':android:services:fcm', @@ -42,6 +45,7 @@ include ':core:sdk', project(':core:sdk').name = 'stitch-core-sdk' project(':core:admin-client').name = 'stitch-core-admin-client' project(':core:services').name = 'core-services' +project(':core:services:aws').name = 'stitch-core-services-aws' project(':core:services:aws-s3').name = 'stitch-core-services-aws-s3' project(':core:services:aws-ses').name = 'stitch-core-services-aws-ses' project(':core:services:fcm').name = 'stitch-core-services-fcm' @@ -54,6 +58,7 @@ project(':server:sdk').name = 'stitch-server-sdk' project(':server:core').name = 'stitch-server-core' project(':server:coretest').name = 'stitch-server-coretest' project(':server:services').name = 'server-services' +project(':server:services:aws').name = 'stitch-server-services-aws' project(':server:services:aws-s3').name = 'stitch-server-services-aws-s3' project(':server:services:aws-ses').name = 'stitch-server-services-aws-ses' project(':server:services:fcm').name = 'stitch-server-services-fcm' @@ -66,6 +71,7 @@ project(':android:sdk').name = 'stitch-android-sdk' project(':android:core').name = 'stitch-android-core' project(':android:coretest').name = 'stitch-android-coretest' project(':android:services').name = 'android-services' +project(':android:services:aws').name = 'stitch-android-services-aws' project(':android:services:aws-s3').name = 'stitch-android-services-aws-s3' project(':android:services:aws-ses').name = 'stitch-android-services-aws-ses' project(':android:services:fcm').name = 'stitch-android-services-fcm'