diff --git a/core/api/core.api b/core/api/core.api index 8eb6966e3..83a435287 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -135,6 +135,10 @@ public class com/dropbox/core/DbxPKCEWebAuth { public fun finishFromRedirect (Ljava/lang/String;Lcom/dropbox/core/DbxSessionStore;Ljava/util/Map;)Lcom/dropbox/core/DbxAuthFinish; } +public abstract interface class com/dropbox/core/DbxRequest { + public abstract fun call ()Ljava/lang/Object; +} + public class com/dropbox/core/DbxRequestConfig { public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;Ljava/lang/String;)V diff --git a/core/build.gradle b/core/build.gradle index 1fe17fe26..cfee3c2e7 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -306,6 +306,7 @@ tasks.named("generateTestStone", StoneTask) { ), new StoneConfig( packageName: packageName, + generateRequest: true, client: new ClientSpec( name: 'DbxClientV2Base', javadoc: 'TestClass.', diff --git a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/DbxTestTestRequests.java b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/DbxTestTestRequests.java index 0a48582cb..f155bcf14 100644 --- a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/DbxTestTestRequests.java +++ b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/DbxTestTestRequests.java @@ -6,6 +6,7 @@ import com.dropbox.core.DbxApiException; import com.dropbox.core.DbxDownloader; import com.dropbox.core.DbxException; +import com.dropbox.core.DbxRequest; import com.dropbox.core.DbxUploader; import com.dropbox.core.DbxWrappedException; import com.dropbox.core.http.HttpRequestor; @@ -77,6 +78,21 @@ public DbxDownloader testDownload(String name, String breed) throws DbxApi return testDownload(_arg, Collections.emptyList()); } + /** + * See {@link DbxTestTestRequests#testDownload(String,String)}. + * + * @param name Used in {@link + * DbxTestTestRequests#testRouteV2(String,Date)}. Must not be {@code + * null}. + * @param breed Must not be {@code null}. + * + * @return A {@link DbxRequest} that can be executed later to obtain the + * {@code DbxDownloader<Fish>}. + */ + public DbxRequest> testDownloadRequest(String name, String breed) { + return () -> testDownload(name, breed); + } + /** * * @param name Used in {@link @@ -138,6 +154,20 @@ public DbxDownloader testDownloadV2(UninitializedReason reason, String ses return testDownloadV2(_arg, Collections.emptyList()); } + /** + * See {@link + * DbxTestTestRequests#testDownloadV2(UninitializedReason,String)}. + * + * @param reason Must not be {@code null}. + * @param sessionId Must not be {@code null}. + * + * @return A {@link DbxRequest} that can be executed later to obtain the + * {@code DbxDownloader<Fish>}. + */ + public DbxRequest> testDownloadV2Request(UninitializedReason reason, String sessionId) { + return () -> testDownloadV2(reason, sessionId); + } + /** * * @param reason Must not be {@code null}. @@ -175,6 +205,15 @@ public void testRoute() throws DbxApiException, DbxException { } } + /** + * See {@link DbxTestTestRequests#testRoute}. + * + * @return A {@link DbxRequest} that can be executed later. + */ + public DbxRequest testRouteRequest() { + return () -> { testRoute(); return null; }; + } + // // route 2/test/test_route_v2 // @@ -211,6 +250,19 @@ public void testRouteV2(String name) throws ParentUnionException, DbxException { testRouteV2(_arg); } + /** + * See {@link DbxTestTestRequests#testRouteV2(String,Date)}. + * + * @param name Used in {@link + * DbxTestTestRequests#testRouteV2(String,Date)}. Must not be {@code + * null}. + * + * @return A {@link DbxRequest} that can be executed later. + */ + public DbxRequest testRouteV2Request(String name) { + return () -> { testRouteV2(name); return null; }; + } + /** * * @param name Used in {@link @@ -225,6 +277,19 @@ public void testRouteV2(String name, Date born) throws ParentUnionException, Dbx testRouteV2(_arg); } + /** + * See {@link DbxTestTestRequests#testRouteV2(String,Date)}. + * + * @param name Used in {@link + * DbxTestTestRequests#testRouteV2(String,Date)}. Must not be {@code + * null}. + * + * @return A {@link DbxRequest} that can be executed later. + */ + public DbxRequest testRouteV2Request(String name, Date born) { + return () -> { testRouteV2(name, born); return null; }; + } + // // route 2/test/test_upload // @@ -258,6 +323,19 @@ public TestUploadUploader testUpload(UninitializedReason reason, String sessionI return testUpload(_arg); } + /** + * See {@link DbxTestTestRequests#testUpload(UninitializedReason,String)}. + * + * @param reason Must not be {@code null}. + * @param sessionId Must not be {@code null}. + * + * @return A {@link DbxRequest} that can be executed later to obtain the + * {@code TestUploadUploader}. + */ + public DbxRequest testUploadRequest(UninitializedReason reason, String sessionId) { + return () -> testUpload(reason, sessionId); + } + // // route 2/test/test_upload_v2 // @@ -293,6 +371,21 @@ public TestUploadV2Uploader testUploadV2(String name, String breed) throws DbxEx return testUploadV2(_arg); } + /** + * See {@link DbxTestTestRequests#testUploadV2(String,String)}. + * + * @param name Used in {@link + * DbxTestTestRequests#testRouteV2(String,Date)}. Must not be {@code + * null}. + * @param breed Must not be {@code null}. + * + * @return A {@link DbxRequest} that can be executed later to obtain the + * {@code TestUploadV2Uploader}. + */ + public DbxRequest testUploadV2Request(String name, String breed) { + return () -> testUploadV2(name, breed); + } + /** * * @param name Used in {@link @@ -346,6 +439,21 @@ public TestUploadV3Uploader testUploadV3(String name, String breed) throws DbxEx return testUploadV3(_arg); } + /** + * See {@link DbxTestTestRequests#testUploadV3(String,String)}. + * + * @param name Used in {@link + * DbxTestTestRequests#testRouteV2(String,Date)}. Must not be {@code + * null}. + * @param breed Must not be {@code null}. + * + * @return A {@link DbxRequest} that can be executed later to obtain the + * {@code TestUploadV3Uploader}. + */ + public DbxRequest testUploadV3Request(String name, String breed) { + return () -> testUploadV3(name, breed); + } + /** * * @param name Used in {@link diff --git a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/DbxTestTestUploadV3Builder.java b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/DbxTestTestUploadV3Builder.java index e8703bf9b..68137bf34 100644 --- a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/DbxTestTestUploadV3Builder.java +++ b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/DbxTestTestUploadV3Builder.java @@ -4,6 +4,7 @@ package com.dropbox.core.stone.test; import com.dropbox.core.DbxException; +import com.dropbox.core.DbxRequest; import com.dropbox.core.util.LangUtil; import com.dropbox.core.v2.DbxUploadStyleBuilder; @@ -65,4 +66,14 @@ public TestUploadV3Uploader start() throws ParentUnionException, DbxException { Dog arg_ = this._builder.build(); return _client.testUploadV3(arg_); } + + /** + * See {@link DbxTestTestRequests#testUploadV3(String,String)}. + * + * @return A {@link DbxRequest} that can be executed later to obtain the + * {@code TestUploadV3Uploader}. + */ + public DbxRequest startRequest() { + return () -> start(); + } } diff --git a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestDownloadBuilder.java b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestDownloadBuilder.java index a12567d07..4be0b7571 100644 --- a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestDownloadBuilder.java +++ b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestDownloadBuilder.java @@ -6,6 +6,7 @@ import com.dropbox.core.DbxApiException; import com.dropbox.core.DbxDownloader; import com.dropbox.core.DbxException; +import com.dropbox.core.DbxRequest; import com.dropbox.core.util.LangUtil; import com.dropbox.core.v2.DbxDownloadStyleBuilder; @@ -67,4 +68,14 @@ public DbxDownloader start() throws DbxApiException, DbxException { Dog arg_ = this._builder.build(); return _client.testDownload(arg_, getHeaders()); } + + /** + * See {@link DbxTestTestRequests#testDownload(String,String)}. + * + * @return A {@link DbxRequest} that can be executed later to obtain the + * {@code DbxDownloader<Fish>}. + */ + public DbxRequest> startRequest() { + return () -> start(); + } } diff --git a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestDownloadV2Builder.java b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestDownloadV2Builder.java index 4fa1a1297..e2baf7a91 100644 --- a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestDownloadV2Builder.java +++ b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestDownloadV2Builder.java @@ -5,6 +5,7 @@ import com.dropbox.core.DbxDownloader; import com.dropbox.core.DbxException; +import com.dropbox.core.DbxRequest; import com.dropbox.core.v2.DbxDownloadStyleBuilder; /** @@ -46,4 +47,15 @@ public DbxDownloader start() throws ParentUnionException, DbxException { Uninitialized arg_ = new Uninitialized(reason, sessionId); return _client.testDownloadV2(arg_, getHeaders()); } + + /** + * See {@link + * DbxTestTestRequests#testDownloadV2(UninitializedReason,String)}. + * + * @return A {@link DbxRequest} that can be executed later to obtain the + * {@code DbxDownloader<Fish>}. + */ + public DbxRequest> startRequest() { + return () -> start(); + } } diff --git a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestUploadV2Builder.java b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestUploadV2Builder.java index ac4044c9e..49cc57e49 100644 --- a/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestUploadV2Builder.java +++ b/core/build/generated_stone_source/test/src/com/dropbox/core/stone/test/TestUploadV2Builder.java @@ -4,6 +4,7 @@ package com.dropbox.core.stone.test; import com.dropbox.core.DbxException; +import com.dropbox.core.DbxRequest; import com.dropbox.core.util.LangUtil; import com.dropbox.core.v2.DbxUploadStyleBuilder; @@ -65,4 +66,14 @@ public TestUploadV2Uploader start() throws ParentUnionException, DbxException { Dog arg_ = this._builder.build(); return _client.testUploadV2(arg_); } + + /** + * See {@link DbxTestTestRequests#testUploadV2(String,String)}. + * + * @return A {@link DbxRequest} that can be executed later to obtain the + * {@code TestUploadV2Uploader}. + */ + public DbxRequest startRequest() { + return () -> start(); + } } diff --git a/core/generator/java/java.stoneg.py b/core/generator/java/java.stoneg.py index 544b06896..bcda84dd8 100644 --- a/core/generator/java/java.stoneg.py +++ b/core/generator/java/java.stoneg.py @@ -621,6 +621,8 @@ def __lt__(self, other): 'exist.') _CMDLINE_PARSER.add_argument('--unused-classes-to-generate', default=None, help='Specify types ' + 'that we want to generate regardless of whether they are used.') +_CMDLINE_PARSER.add_argument('--generate-request', action="store_true", default=False, + help='Generate additional *Request methods that return DbxRequest.') class JavaCodeGenerator(CodeBackend): @@ -729,6 +731,8 @@ def add_imports_for_namespace(self, namespace): 'java.util.HashMap', 'java.util.Map', ) + if self._j._args.generate_request: + self.add_imports('com.dropbox.core.DbxRequest') for route in namespace.routes: self.add_imports_for_route(route) @@ -795,6 +799,9 @@ def add_imports_for_route_builder(self, route): elif j.request_style(route) == 'upload': self.add_imports('com.dropbox.core.v2.DbxUploadStyleBuilder') + if self._j._args.generate_request: + self.add_imports('com.dropbox.core.DbxRequest') + def add_imports_for_route_uploader(self, route): self.add_imports( 'com.dropbox.core.DbxWrappedException', @@ -2800,6 +2807,12 @@ def generate_route_base(self, route, force_public=False): else: assert False, "unrecognized route request style: %s" % j.request_style(route) + if is_public and self.g.args.generate_request: + request_args = w.fmt('%s arg', j.java_class(route.arg_data_type)) if j.has_arg(route) else '' + request_arg_names = 'arg' if j.has_arg(route) else '' + self._emit_request_method_body(route, request_args, request_arg_names, return_class, + params=params) + def generate_route(self, route, required_only=True): assert isinstance(route, ApiRoute), repr(route) @@ -2914,6 +2927,15 @@ def generate_route(self, route, required_only=True): else: w.out('%s(_arg);', j.route_method(route)) + if self.g.args.generate_request: + request_args = ', '.join( + w.fmt('%s %s', j.java_class(f), j.param_name(f)) for f in fields + ) + request_arg_names = ', '.join(j.param_name(f) for f in fields) + request_params = w._javadoc_fields(fields, route, allow_defaults=False) + self._emit_request_method_body(route, request_args, request_arg_names, return_class, + params=request_params) + def generate_route_builder_method(self, route): assert isinstance(route, ApiRoute), repr(route) @@ -2953,6 +2975,40 @@ def generate_route_builder_method(self, route): else: w.out('return new %s(this, %s);', return_class, builder_args) + def _emit_request_method_body(self, route, args, arg_names, return_class, + fields=(), params=(), method_name=None, delegate=None): + """Emit the request method signature and body.""" + w = self.w + j = self.j + + if return_class == JavaClass('void'): + boxed_return = JavaClass('java.lang.Void') + else: + boxed_return = return_class + + request_class = JavaClass('com.dropbox.core.DbxRequest', generics=(boxed_return,)) + if method_name is None: + method_name = j.route_method(route) + 'Request' + if delegate is None: + delegate = '%s(%s)' % (j.route_method(route), arg_names) + + w.out('') + dbx_request_ref = w.javadoc_ref(JavaClass('com.dropbox.core.DbxRequest')) + if return_class == JavaClass('void'): + returns_doc = "A %s that can be executed later." % dbx_request_ref + else: + result_type = w.resolved_class(boxed_return, generics=True) + returns_doc = "A %s that can be executed later to obtain the {@code %s}." % ( + dbx_request_ref, result_type, + ) + doc = "See %s." % w.javadoc_ref(route) + w.javadoc(doc, params=params, returns=returns_doc) + with w.block('public %s %s(%s)', request_class, method_name, args): + if return_class == JavaClass('void'): + w.out('return () -> { %s; return null; };', delegate) + else: + w.out('return () -> %s;', delegate) + def translate_error_wrapper(self, route, error_wrapper_var): assert isinstance(route, ApiRoute), repr(route) assert isinstance(error_wrapper_var, str), repr(error_wrapper_var) @@ -3848,6 +3904,10 @@ def generate_route_builder(self, route): else: w.out('_client.%s(%s);', j.route_method(route), ', '.join(args)) + if self.g.args.generate_request: + self._emit_request_method_body(route, '', '', return_class, + method_name='startRequest', delegate='start()') + def generate_field_assignment(self, field, lhs=None, rhs=None, allow_default=True): assert isinstance(field, Field), repr(field) diff --git a/core/src/main/java/com/dropbox/core/DbxRequest.java b/core/src/main/java/com/dropbox/core/DbxRequest.java new file mode 100644 index 000000000..a4f5acaba --- /dev/null +++ b/core/src/main/java/com/dropbox/core/DbxRequest.java @@ -0,0 +1,19 @@ +package com.dropbox.core; + +/** + * A functional interface representing a request that produces a result {@code } and may throw a checked + * exception. Similar to {@link java.util.concurrent.Callable}, but intended for use in contexts + * where the operation represents an API request. + * + * @param the type of result produced by this request + */ +@FunctionalInterface +public interface DbxRequest { + /** + * Executes the request and returns the result. + * + * @return the result of the request + * @throws Exception if the request fails + */ + T call() throws Exception; +} diff --git a/core/src/test/java/com/dropbox/core/stone/DbxRequestMethodTest.java b/core/src/test/java/com/dropbox/core/stone/DbxRequestMethodTest.java new file mode 100644 index 000000000..825aa887d --- /dev/null +++ b/core/src/test/java/com/dropbox/core/stone/DbxRequestMethodTest.java @@ -0,0 +1,55 @@ +package com.dropbox.core.stone; + +import static org.mockito.Mockito.*; + +import com.dropbox.core.DbxDownloader; +import com.dropbox.core.DbxRequest; +import com.dropbox.core.stone.test.DbxTestTestRequests; +import com.dropbox.core.stone.test.Fish; +import com.dropbox.core.stone.test.TestUploadUploader; +import com.dropbox.core.stone.test.UninitializedReason; +import com.dropbox.core.v2.DbxRawClientV2; + +import org.mockito.Mockito; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +public class DbxRequestMethodTest { + + @Test + public void testVoidRouteRequestReturnsDbxRequest() { + DbxRawClientV2 mockClient = mock(DbxRawClientV2.class, RETURNS_DEEP_STUBS); + DbxTestTestRequests requests = new DbxTestTestRequests(mockClient); + + DbxRequest request = requests.testRouteRequest(); + assertNotNull(request); + } + + @Test + public void testDownloadRouteRequestReturnsDbxRequest() { + DbxRawClientV2 mockClient = mock(DbxRawClientV2.class, RETURNS_DEEP_STUBS); + DbxTestTestRequests requests = new DbxTestTestRequests(mockClient); + + DbxRequest> request = requests.testDownloadRequest("fido", "lab"); + assertNotNull(request); + } + + @Test + public void testUploadRouteRequestReturnsDbxRequest() { + DbxRawClientV2 mockClient = mock(DbxRawClientV2.class, RETURNS_DEEP_STUBS); + DbxTestTestRequests requests = new DbxTestTestRequests(mockClient); + + DbxRequest request = requests.testUploadRequest(UninitializedReason.BAD_REQUEST, "session123"); + assertNotNull(request); + } + + @Test + public void testRouteV2RequestWithParams() { + DbxRawClientV2 mockClient = mock(DbxRawClientV2.class, RETURNS_DEEP_STUBS); + DbxTestTestRequests requests = new DbxTestTestRequests(mockClient); + + DbxRequest request = requests.testRouteV2Request("buddy"); + assertNotNull(request); + } +} diff --git a/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StoneTask.kt b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StoneTask.kt index f202d1c43..13a794aa6 100644 --- a/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StoneTask.kt +++ b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/StoneTask.kt @@ -125,6 +125,10 @@ abstract class StoneTask : DefaultTask() { generatorArgs.addAll(generatorArgs.indexOf(":all") + 1, listOf("--filter-by-route-attr", buildRouteFilter(stoneConfig))) } + if (stoneConfig.generateRequest) { + generatorArgs += "--generate-request" + } + if (stoneConfig.dataTypesOnly) { generatorArgs += "--data-types-only" } else { diff --git a/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/StoneConfig.kt b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/StoneConfig.kt index 501925ea5..913abbea3 100644 --- a/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/StoneConfig.kt +++ b/stone-java-gradle-plugin/src/main/kotlin/com/dropbox/stone/java/model/StoneConfig.kt @@ -6,6 +6,7 @@ class StoneConfig( var packageName: String = "com.dropbox.stone", var globalRouteFilter: String? = null, var dataTypesOnly: Boolean = false, + var generateRequest: Boolean = false, var client: ClientSpec? = null, var routeWhitelistFilter: String? = null, ) : Serializable \ No newline at end of file