├── .editorconfig ├── .github └── workflows │ ├── ci-release.yml │ ├── ci-snapshot.yml │ └── ci-test.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── build.gradle.kts ├── docs ├── README.md ├── _config.yml └── javadoc │ ├── allclasses-index.html │ ├── allclasses.html │ ├── allpackages-index.html │ ├── constant-values.html │ ├── deprecated-list.html │ ├── element-list │ ├── help-doc.html │ ├── index-all.html │ ├── index.html │ ├── io │ └── reactiverse │ │ └── awssdk │ │ ├── VertxExecutor.html │ │ ├── VertxNioAsyncHttpClient.html │ │ ├── VertxSdkClient.html │ │ ├── converters │ │ ├── MethodConverter.html │ │ ├── package-summary.html │ │ └── package-tree.html │ │ ├── package-summary.html │ │ ├── package-tree.html │ │ └── reactivestreams │ │ ├── HttpClientRequestSubscriber.html │ │ ├── ReadStreamPublisher.html │ │ ├── WriteStreamSubscriber.html │ │ ├── package-summary.html │ │ └── package-tree.html │ ├── jquery │ ├── external │ │ └── jquery │ │ │ └── jquery.js │ ├── images │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ ├── ui-icons_222222_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_454545_256x240.png │ │ ├── ui-icons_888888_256x240.png │ │ └── ui-icons_cd0a0a_256x240.png │ ├── jquery-3.3.1.js │ ├── jquery-migrate-3.0.1.js │ ├── jquery-ui.css │ ├── jquery-ui.js │ ├── jquery-ui.min.css │ ├── jquery-ui.min.js │ ├── jquery-ui.structure.css │ ├── jquery-ui.structure.min.css │ ├── jszip-utils │ │ └── dist │ │ │ ├── jszip-utils-ie.js │ │ │ ├── jszip-utils-ie.min.js │ │ │ ├── jszip-utils.js │ │ │ └── jszip-utils.min.js │ └── jszip │ │ └── dist │ │ ├── jszip.js │ │ └── jszip.min.js │ ├── member-search-index.js │ ├── member-search-index.zip │ ├── overview-summary.html │ ├── overview-tree.html │ ├── package-search-index.js │ ├── package-search-index.zip │ ├── resources │ ├── glass.png │ └── x.png │ ├── script.js │ ├── search.js │ ├── stylesheet.css │ ├── type-search-index.js │ └── type-search-index.zip ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src ├── main │ └── java │ │ └── io │ │ └── reactiverse │ │ └── awssdk │ │ ├── VertxExecutor.java │ │ ├── VertxNioAsyncHttpClient.java │ │ ├── VertxSdkClient.java │ │ ├── converters │ │ ├── MethodConverter.java │ │ └── VertxAsyncResponseTransformer.java │ │ └── reactivestreams │ │ ├── HttpClientRequestSubscriber.java │ │ ├── ReadStreamPublisher.java │ │ └── WriteStreamSubscriber.java └── test │ ├── java │ └── io │ │ └── reactiverse │ │ └── awssdk │ │ ├── AsyncHttpClientTest.java │ │ ├── ContextAssertVertxNioAsyncHttpClient.java │ │ ├── ProxyTest.java │ │ ├── RetryContextTest.java │ │ ├── integration │ │ ├── LocalStackBaseSpec.java │ │ ├── apigateway │ │ │ └── VertxApiGatewaySpec.java │ │ ├── cloudwatch │ │ │ └── VertxCloudWatchClientSpec.java │ │ ├── dynamodb │ │ │ └── VertxDynamoClientSpec.java │ │ ├── firehose │ │ │ └── VertxFirehoseClientSpec.java │ │ ├── kinesis │ │ │ └── VertxKinesisClientSpec.java │ │ ├── lambda │ │ │ └── VertxLambdaClientSpec.java │ │ └── s3 │ │ │ └── VertxS3ClientSpec.java │ │ └── utils │ │ └── ZipUtils.java │ └── resources │ ├── lambda │ └── greeter.py │ ├── logback.xml │ └── s3 │ └── cairn_little.jpg └── utils └── Dockerfile.test /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | trim_trailing_whitespace = true 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | [**/examples/**.java] 12 | # 84 looks like a odd number, however 13 | # it accounts for 4 spaces (class and example method indentation) 14 | max_line_length = 84 15 | -------------------------------------------------------------------------------- /.github/workflows/ci-release.yml: -------------------------------------------------------------------------------- 1 | # When a tag matching a semver number (like `1.1.0`) is pushed, then try to publish the version to bintray and create the matching release in Github 2 | name: CI-Release 3 | on: 4 | push: 5 | tags: 6 | - '*-RELEASE' 7 | 8 | jobs: 9 | publish-release-sonatype: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Setup java 15 | uses: actions/setup-java@v3 16 | with: 17 | distribution: temurin 18 | java-version: 11 19 | 20 | - name: Setup Gradle 21 | uses: gradle/gradle-build-action@v2 22 | with: 23 | gradle-version: current 24 | 25 | - name: Get Project version 26 | id: get_version 27 | run: echo VERSION=$(gradle properties --no-daemon --console=plain -q | grep "^version:" | awk '{printf $2}') >> "$GITHUB_OUTPUT" 28 | 29 | - name: Decode 30 | run: | 31 | echo "${{secrets.SIGNING_SECRET_KEY_RING_FILE}}" > /tmp/secring.gpg.b64 32 | base64 -d /tmp/secring.gpg.b64 > /tmp/secring.gpg 33 | 34 | - name: Publish Release to Sonatype 35 | uses: gradle/gradle-build-action@v2 36 | with: 37 | gradle-version: current 38 | arguments: assemble publish -Psigning.keyId=${{secrets.SIGNING_KEY_ID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -Psigning.secretKeyRingFile=/tmp/secring.gpg -PossrhUsername=${{secrets.SONATYPE_NEXUS_USERNAME}} -PossrhPassword=${{secrets.SONATYPE_NEXUS_PASSWORD}} --stacktrace 39 | 40 | - name: Create Release in Github 41 | uses: softprops/action-gh-release@v1 42 | with: 43 | tag_name: ${{ steps.get_version.outputs.VERSION }} 44 | name: ${{ steps.get_version.outputs.VERSION }} 45 | generate_release_notes: true 46 | body: | 47 | Available in Sonatype repository under: 48 | [`io.reactiverse:aws-sdk:${{ steps.get_version.outputs.VERSION }}`](https://central.sonatype.com/artifact/io.reactiverse/aws-sdk/${{ steps.get_version.outputs.VERSION }}) 49 | draft: false 50 | prerelease: false 51 | -------------------------------------------------------------------------------- /.github/workflows/ci-snapshot.yml: -------------------------------------------------------------------------------- 1 | name: CI-Snapshot 2 | on: 3 | push: 4 | tags: 5 | - '*-SNAPSHOT' 6 | 7 | jobs: 8 | publish-snapshot-sonatype: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | - name: Setup java 14 | uses: actions/setup-java@v3 15 | with: 16 | distribution: temurin 17 | java-version: 11 18 | 19 | - name: Setup Gradle 20 | uses: gradle/gradle-build-action@v2 21 | with: 22 | gradle-version: current 23 | 24 | - name: Get Project version 25 | id: get_version 26 | run: echo VERSION=$(gradle properties --no-daemon --console=plain -q | grep "^version:" | awk '{printf $2}') >> "$GITHUB_OUTPUT" 27 | 28 | - name: Decode 29 | run: | 30 | echo "${{secrets.SIGNING_SECRET_KEY_RING_FILE}}" > /tmp/secring.gpg.b64 31 | base64 -d /tmp/secring.gpg.b64 > /tmp/secring.gpg 32 | 33 | - name: Publish Snapshot to Sonatype 34 | uses: gradle/gradle-build-action@v2 35 | with: 36 | gradle-version: current 37 | arguments: assemble publish -Psigning.keyId=${{secrets.SIGNING_KEY_ID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -Psigning.secretKeyRingFile=/tmp/secring.gpg -PossrhUsername=${{secrets.SONATYPE_NEXUS_USERNAME}} -PossrhPassword=${{secrets.SONATYPE_NEXUS_PASSWORD}} --stacktrace 38 | 39 | - name: Create Release in Github 40 | uses: softprops/action-gh-release@v1 41 | with: 42 | tag_name: ${{ steps.get_version.outputs.VERSION }} 43 | name: ${{ steps.get_version.outputs.VERSION }} 44 | generate_release_notes: true 45 | body: | 46 | Available in Sonatype SNAPSHOTs repository under: 47 | `io.reactiverse:aws-sdk:${{ steps.get_version.outputs.VERSION }}` 48 | draft: true 49 | prerelease: true 50 | -------------------------------------------------------------------------------- /.github/workflows/ci-test.yml: -------------------------------------------------------------------------------- 1 | # Always tests on every branch push / PR / master 2 | name: CI-Test 3 | on: push 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | java: [ 8, 11, 17, 19 ] # LTS versions + latest version 11 | name: "Run tests with JDK ${{ matrix.java }}" 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-java@v3 15 | with: 16 | distribution: temurin 17 | java-version: ${{ matrix.java }} 18 | 19 | - name: Pre-Pull localstack image 20 | run: "docker pull localstack/localstack:0.14" 21 | 22 | 23 | - uses: gradle/gradle-build-action@v2 24 | with: 25 | gradle-version: current 26 | arguments: test -Dtests.integration=localstack 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | .gradle 3 | .idea 4 | target 5 | build 6 | .vertx 7 | .DS_Store 8 | .run 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Use AWS SDK v2 with Vert.x 2 | 3 | [![Build Status](https://travis-ci.org/reactiverse/aws-sdk.svg?branch=master)](https://travis-ci.org/reactiverse/aws-sdk) 4 | 5 | This project provides a `VertxNioAsyncHttpClient` and a `VertxExecutor` so that you can use AWS SDK v2 in a Vert.x context. 6 | 7 | ## Coordinates 8 | 9 | Artifacts are published [here](https://search.maven.org/artifact/io.reactiverse/aws-sdk) 10 | 11 | ## Version compatibility matrix 12 | 13 | | Project | Vert.x | AWS sdk | 14 | |---------|--------|----------| 15 | | 1.2.2 | 4.4.5 | 2.20.138 | 16 | | 1.2.1 | 4.4.2 | 2.20.74 | 17 | | 1.2.0 | 4.4.0 | 2.20.2 | 18 | | 1.1.0 | 4.2.4 | 2.17.129 | 19 | | 1.0.0 | 4.0.0 | 2.15.45 | 20 | | 0.7.0 | 3.9.4 | 2.15.23 | 21 | | 0.6.0 | 3.9.2 | 2.14.7 | 22 | | 0.5.1 | 3.9.2 | 2.13.6 | 23 | | 0.5.0 | 3.9.0 | 2.12.0 | 24 | | 0.4.0 | 3.8.3 | 2.10.16 | 25 | | 0.3.0 | 3.8.1 | 2.7.8 | 26 | 27 | ## Documentation 28 | 29 | See [this page](https://reactiverse.io/aws-sdk) 30 | 31 | ## Motivations 32 | 33 | ### AWS SDK v1 => blocking IOs 34 | 35 | As you know, Vert.x uses non-blocking IO. This means, among other stuff, that you should never ever block the event-loop. 36 | AWS SDK v1 implementation relies on blocking IOs. This means you cannot use it together with Vert.x in a straightforward 37 | way. You would end up blocking the event-loop, hence killing your application's scalability. The only option would be 38 | to wrap your synchronous calls to AWS SDK v1 within `executeBlocking` or use a worker thread. 39 | 40 | Even though some methods of the AWS SDK are indicated as "async" (`DynamoAsyncClient` for instance), it internally uses 41 | a thread pool whose size is configurable. Those threads can be a bottleneck in your application 42 | 43 | You cannot really use AWS SDK v1 together with Vert.x in a non-blocking scalable way. 44 | 45 | ### Embrace AWS SDK v2 46 | 47 | Since 2018, AWS has published the version 2 of its SDK, embracing non-blocking IO model. 48 | 49 | Now you can use V2 together with Vert.x using this project. 50 | 51 | * using Vert.x's HTTP client 52 | * `CompletableFuture`'s are executed in the same Vert.x context that the one that made the request 53 | 54 | ## Contributing 55 | 56 | Tests placed under the `io.vertx.ext.awssdk.integration` package are using `localstack`: a huge set of 57 | utilities (docker images) emulating AWS Services (DynamoDB, Kinesis, S3, etc.). 58 | 59 | In order to do so, they require a local docker daemon running on the machine. 60 | 61 | They will download docker images from the docker hub, run the appropriate service as a docker container, then test 62 | the code against this local docker container. 63 | 64 | They'll only be executed if the system property `tests.integration` is set to `localstack`. They'll be ignored otherwise. 65 | 66 | ## Documentation 67 | 68 | Documentation is `docs/README.md` and visible at https://github.com/reactiverse/aws-sdk/tree/master/docs or https://reactiverse.io/aws-sdk/ 69 | 70 | Javadoc can be produced (with Java 11 otherwise it does not link Vert.x API docs) 71 | 72 | ``` 73 | > ./gradlew javadocToDocsFolder 74 | ``` 75 | 76 | This will update the docs/javadoc with latest javadocs 77 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | val vertxVersion = "4.4.5" 2 | val awsSdkVersion = "2.20.138" 3 | val junit5Version = "5.8.2" 4 | val logbackVersion = "1.2.10" 5 | val localstackVersion = "0.2.22" 6 | val integrationOption = "tests.integration" 7 | 8 | group = "io.reactiverse" 9 | version = "1.2.2" 10 | 11 | plugins { 12 | `java-library` 13 | `maven-publish` 14 | signing 15 | jacoco 16 | id("org.sonarqube") version "3.3" 17 | id("com.github.ben-manes.versions") version "0.42.0" 18 | } 19 | 20 | // In order to publish SNAPSHOTs to Sonatype Snapshots repository => the CI should define such `ossrhUsername` and `ossrhPassword` properties 21 | if (!project.hasProperty("ossrhUsername")) { 22 | logger.warn("No ossrhUsername property defined in your Gradle properties file to deploy to Sonatype Snapshots, using 'foo' to make the build pass") 23 | project.extra["ossrhUsername"] = "foo" 24 | } 25 | if (!project.hasProperty("ossrhPassword")) { 26 | logger.warn("No ossrhPassword property defined in your Gradle properties file to deploy to Sonatype Snapshots, using 'bar' to make the build pass") 27 | project.extra["ossrhPassword"] = "bar" 28 | } 29 | 30 | extra["isReleaseVersion"] = !version.toString().endsWith("SNAPSHOT") 31 | 32 | repositories { 33 | mavenCentral() 34 | maven { 35 | url = uri("https://oss.sonatype.org/content/repositories/snapshots") 36 | } 37 | } 38 | 39 | fun isNonStable(version: String): Boolean { 40 | val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) } 41 | val regex = "^[0-9,.v-]+(-r)?$".toRegex() 42 | val isStable = stableKeyword || regex.matches(version) 43 | return isStable.not() 44 | } 45 | 46 | dependencies { 47 | api("io.vertx:vertx-core:$vertxVersion") 48 | api("software.amazon.awssdk:aws-core:$awsSdkVersion") 49 | 50 | testImplementation("io.vertx:vertx-junit5:$vertxVersion") 51 | testImplementation("io.vertx:vertx-rx-java2:$vertxVersion") 52 | testImplementation("cloud.localstack:localstack-utils:$localstackVersion") 53 | testImplementation("ch.qos.logback:logback-classic:$logbackVersion") 54 | testImplementation("ch.qos.logback:logback-core:$logbackVersion") 55 | testImplementation("software.amazon.awssdk:aws-sdk-java:$awsSdkVersion") 56 | testImplementation("org.junit.jupiter:junit-jupiter-engine:$junit5Version") 57 | } 58 | 59 | java { 60 | sourceCompatibility = JavaVersion.VERSION_1_8 61 | targetCompatibility = JavaVersion.VERSION_1_8 62 | } 63 | 64 | jacoco { 65 | toolVersion = "0.8.7" 66 | } 67 | 68 | tasks { 69 | named("dependencyUpdates", com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask::class.java).configure { 70 | rejectVersionIf { 71 | isNonStable(candidate.version) 72 | } 73 | } 74 | 75 | jacocoTestReport { 76 | dependsOn(":test") 77 | reports { 78 | xml.required.set(true) 79 | csv.required.set(false) 80 | html.outputLocation.set(file("$buildDir/jacocoHtml")) 81 | } 82 | } 83 | 84 | withType { 85 | useJUnitPlatform() 86 | systemProperties[integrationOption] = System.getProperty(integrationOption) 87 | maxParallelForks = 1 88 | } 89 | 90 | create("javadocToDocsFolder") { 91 | from(javadoc) 92 | into("docs/javadoc") 93 | } 94 | 95 | assemble { 96 | dependsOn("javadocToDocsFolder") 97 | } 98 | 99 | create("sourcesJar") { 100 | from(sourceSets.main.get().allJava) 101 | archiveClassifier.set("sources") 102 | } 103 | 104 | create("javadocJar") { 105 | from(javadoc) 106 | archiveClassifier.set("javadoc") 107 | } 108 | 109 | javadoc { 110 | if (JavaVersion.current().isJava9Compatible) { 111 | (options as StandardJavadocDocletOptions).addBooleanOption("html5", true) 112 | } 113 | options { 114 | source("8") 115 | } 116 | (options as StandardJavadocDocletOptions).links( 117 | "https://docs.oracle.com/javase/8/docs/api/", 118 | "https://sdk.amazonaws.com/java/api/latest/", 119 | "https://vertx.io/docs/apidocs/", 120 | "http://www.reactive-streams.org/reactive-streams-1.0.0-javadoc/", 121 | "https://netty.io/4.1/api/" 122 | ) 123 | } 124 | 125 | withType { 126 | onlyIf { project.extra["isReleaseVersion"] as Boolean } 127 | } 128 | 129 | withType { 130 | gradleVersion = "8.0" 131 | } 132 | 133 | withType { 134 | options.compilerArgs.add("-Xlint:deprecation") 135 | } 136 | } 137 | 138 | publishing { 139 | publications { 140 | create("mavenJava") { 141 | from(components["java"]) 142 | artifact(tasks["sourcesJar"]) 143 | artifact(tasks["javadocJar"]) 144 | setVersion(project.version) 145 | pom { 146 | name.set(project.name) 147 | description.set("Reactiverse AWS SDK v2 with Vert.x") 148 | url.set("https://github.com/reactiverse/aws-sdk") 149 | licenses { 150 | license { 151 | name.set("The Apache License, Version 2.0") 152 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") 153 | } 154 | } 155 | developers { 156 | developer { 157 | id.set("aesteve") 158 | name.set("Arnaud Esteve") 159 | email.set("arnaud.esteve@gmail.com") 160 | } 161 | } 162 | scm { 163 | connection.set("scm:git:git@github.com:reactiverse/aws-sdk.git") 164 | developerConnection.set("scm:git:git@github.com:reactiverse/aws-sdk.git") 165 | url.set("https://github.com/reactiverse/aws-sdk") 166 | } 167 | } 168 | repositories { 169 | // To locally check out the poms 170 | maven { 171 | val releasesRepoUrl = uri("$buildDir/repos/releases") 172 | val snapshotsRepoUrl = uri("$buildDir/repos/snapshots") 173 | name = "BuildDir" 174 | url = if (project.extra["isReleaseVersion"] as Boolean) releasesRepoUrl else snapshotsRepoUrl 175 | } 176 | maven { 177 | val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") 178 | val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots/") 179 | name = "SonatypeOSS" 180 | url = if (project.extra["isReleaseVersion"] as Boolean) releasesRepoUrl else snapshotsRepoUrl 181 | credentials { 182 | val ossrhUsername: String by project 183 | val ossrhPassword: String by project 184 | username = ossrhUsername 185 | password = ossrhPassword 186 | } 187 | } 188 | } 189 | } 190 | } 191 | } 192 | 193 | signing { 194 | sign(publishing.publications["mavenJava"]) 195 | } 196 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Reactive AWS SDK v2 2 | 3 | This project provides a `VertxNioAsyncHttpClient` and a `VertxExecutor` so that you can use AWS SDK v2 (async, non-blocking) 4 | in a Vert.x application. 5 | 6 | ## Javadoc 7 | 8 | The [Javadoc](./javadoc/index.html). 9 | 10 | ## Install 11 | 12 | Using maven: 13 | ``` 14 | 15 | io.reactiverse 16 | aws-sdk 17 | 1.0.0 18 | 19 | ``` 20 | 21 | Using Gradle: 22 | ``` 23 | implementation("io.reactiverse:aws-sdk:1.0.0") 24 | ``` 25 | 26 | ## How-to 27 | 28 | Given `context` is a Vert.x `Context` object (either obtained by `vertx.getOrCreateContext()` or from 29 | a `AbstractVerticle.init` method), you can use `VertxSdkClient::withVertx` utility method to create a client: 30 | 31 | ```java 32 | DynamoDbAsyncClient dynamo = VertxSdkClient.withVertx( // use the provided utility method 33 | DynamoDbAsyncClient.builder() // with the traditional AwsAsyncClientBuilder you're used to 34 | .region(Region.EU_WEST_1) // that you'll confiugure as usual 35 | , context) // and provide a Vert.x context (the one from within your Verticle for example) 36 | .build(); // then build it => you'll have a Vert.x compatible AwsAsyncClient 37 | ``` 38 | 39 | Under the hood, it's gonna do the following: 40 | 41 | ```java 42 | return builder 43 | .httpClient(new VertxNioAsyncHttpClient(context)) // uses Vert.x's HttpClient to make call to AWS services 44 | .asyncConfiguration(conf -> // tells AWS to execute response callbacks in a Vert.x context 45 | conf.advancedOption(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, new VertxExecutor(context)) 46 | ); 47 | ``` 48 | 49 | * `VertxNioAsyncHttpClient` takes care of making HTTP calls through Vert.x's http client 50 | * `VertxExecutor` provides Vert.x `Context` to `CompletableFuture`s. (see: Vert.x contract regarding contexts and callbacks) 51 | 52 | You can then use your `DynamoDbAsyncClient` as you're used to with AWS SDK. 53 | You can have a look at [integration tests](https://github.com/reactiverse/aws-sdk/blob/master/src/test/java/io/reactiverse/awssdk/integration/) to get many examples. 54 | 55 | 56 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /docs/javadoc/allclasses-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | All Classes (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 40 | 43 |
44 | 100 |
101 |
102 |
103 |

All Classes

104 |
105 |
106 | 147 |
148 |
149 |
150 | 193 |
194 | 195 | 196 | -------------------------------------------------------------------------------- /docs/javadoc/allclasses.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | All Classes (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 |

All Classes

23 |
24 | 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /docs/javadoc/allpackages-index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | All Packages (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

All Packages

98 |
99 |
100 | 125 |
126 |
127 |
128 | 171 |
172 | 173 | 174 | -------------------------------------------------------------------------------- /docs/javadoc/constant-values.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Constant Field Values (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

Constant Field Values

98 |
99 |

Contents

100 | 103 |
104 |
105 |
106 | 107 | 108 |
109 |

io.reactiverse.*

110 | 131 |
132 |
133 |
134 |
135 | 178 |
179 | 180 | 181 | -------------------------------------------------------------------------------- /docs/javadoc/deprecated-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Deprecated List (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

Deprecated API

98 |

Contents

99 |
100 |
101 |
102 | 145 |
146 | 147 | 148 | -------------------------------------------------------------------------------- /docs/javadoc/element-list: -------------------------------------------------------------------------------- 1 | io.reactiverse.awssdk 2 | io.reactiverse.awssdk.converters 3 | io.reactiverse.awssdk.reactivestreams 4 | -------------------------------------------------------------------------------- /docs/javadoc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overview (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |

aws-sdk 0.0.1-SNAPSHOT API

97 |
98 |
99 |
100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
Packages 
PackageDescription
io.reactiverse.awssdk 
io.reactiverse.awssdk.converters 
io.reactiverse.awssdk.reactivestreams 
121 |
122 |
123 |
124 | 167 |
168 | 169 | 170 | -------------------------------------------------------------------------------- /docs/javadoc/io/reactiverse/awssdk/converters/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | io.reactiverse.awssdk.converters (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

Package io.reactiverse.awssdk.converters

98 |
99 |
100 |
    101 |
  • 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
    Class Summary 
    ClassDescription
    MethodConverter 
    115 |
  • 116 |
117 |
118 |
119 |
120 | 163 |
164 | 165 | 166 | -------------------------------------------------------------------------------- /docs/javadoc/io/reactiverse/awssdk/converters/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | io.reactiverse.awssdk.converters Class Hierarchy (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

Hierarchy For Package io.reactiverse.awssdk.converters

98 | Package Hierarchies: 99 | 102 |
103 |
104 |
105 |

Class Hierarchy

106 |
    107 |
  • java.lang.Object 108 | 111 |
  • 112 |
113 |
114 |
115 |
116 |
117 | 160 |
161 | 162 | 163 | -------------------------------------------------------------------------------- /docs/javadoc/io/reactiverse/awssdk/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | io.reactiverse.awssdk (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

Package io.reactiverse.awssdk

98 |
99 |
100 |
    101 |
  • 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
    Interface Summary 
    InterfaceDescription
    VertxSdkClient 
    115 |
  • 116 |
  • 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 129 | 130 | 131 | 132 | 133 | 134 | 135 |
    Class Summary 
    ClassDescription
    VertxExecutor 127 |
    Vertx executor that runs the specified command in the current context.
    128 |
    VertxNioAsyncHttpClient 
    136 |
  • 137 |
138 |
139 |
140 |
141 | 184 |
185 | 186 | 187 | -------------------------------------------------------------------------------- /docs/javadoc/io/reactiverse/awssdk/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | io.reactiverse.awssdk Class Hierarchy (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

Hierarchy For Package io.reactiverse.awssdk

98 | Package Hierarchies: 99 | 102 |
103 |
104 |
105 |

Class Hierarchy

106 | 114 |
115 |
116 |

Interface Hierarchy

117 | 120 |
121 |
122 |
123 |
124 | 167 |
168 | 169 | 170 | -------------------------------------------------------------------------------- /docs/javadoc/io/reactiverse/awssdk/reactivestreams/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | io.reactiverse.awssdk.reactivestreams (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

Package io.reactiverse.awssdk.reactivestreams

98 |
99 |
100 | 125 |
126 |
127 |
128 | 171 |
172 | 173 | 174 | -------------------------------------------------------------------------------- /docs/javadoc/io/reactiverse/awssdk/reactivestreams/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | io.reactiverse.awssdk.reactivestreams Class Hierarchy (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

Hierarchy For Package io.reactiverse.awssdk.reactivestreams

98 | Package Hierarchies: 99 | 102 |
103 |
104 |
105 |

Class Hierarchy

106 | 118 |
119 |
120 |
121 |
122 | 165 |
166 | 167 | 168 | -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/jquery/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /docs/javadoc/jquery/jquery-ui.structure.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.11.4 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | */ 11 | 12 | /* Layout helpers 13 | ----------------------------------*/ 14 | .ui-helper-hidden { 15 | display: none; 16 | } 17 | .ui-helper-hidden-accessible { 18 | border: 0; 19 | clip: rect(0 0 0 0); 20 | height: 1px; 21 | margin: -1px; 22 | overflow: hidden; 23 | padding: 0; 24 | position: absolute; 25 | width: 1px; 26 | } 27 | .ui-helper-reset { 28 | margin: 0; 29 | padding: 0; 30 | border: 0; 31 | outline: 0; 32 | line-height: 1.3; 33 | text-decoration: none; 34 | font-size: 100%; 35 | list-style: none; 36 | } 37 | .ui-helper-clearfix:before, 38 | .ui-helper-clearfix:after { 39 | content: ""; 40 | display: table; 41 | border-collapse: collapse; 42 | } 43 | .ui-helper-clearfix:after { 44 | clear: both; 45 | } 46 | .ui-helper-clearfix { 47 | min-height: 0; /* support: IE7 */ 48 | } 49 | .ui-helper-zfix { 50 | width: 100%; 51 | height: 100%; 52 | top: 0; 53 | left: 0; 54 | position: absolute; 55 | opacity: 0; 56 | filter:Alpha(Opacity=0); /* support: IE8 */ 57 | } 58 | 59 | .ui-front { 60 | z-index: 100; 61 | } 62 | 63 | 64 | /* Interaction Cues 65 | ----------------------------------*/ 66 | .ui-state-disabled { 67 | cursor: default !important; 68 | } 69 | 70 | 71 | /* Icons 72 | ----------------------------------*/ 73 | 74 | /* states and images */ 75 | .ui-icon { 76 | display: block; 77 | text-indent: -99999px; 78 | overflow: hidden; 79 | background-repeat: no-repeat; 80 | } 81 | 82 | 83 | /* Misc visuals 84 | ----------------------------------*/ 85 | 86 | /* Overlays */ 87 | .ui-widget-overlay { 88 | position: fixed; 89 | top: 0; 90 | left: 0; 91 | width: 100%; 92 | height: 100%; 93 | } 94 | .ui-autocomplete { 95 | position: absolute; 96 | top: 0; 97 | left: 0; 98 | cursor: default; 99 | } 100 | .ui-menu { 101 | list-style: none; 102 | padding: 0; 103 | margin: 0; 104 | display: block; 105 | outline: none; 106 | } 107 | .ui-menu .ui-menu { 108 | position: absolute; 109 | } 110 | .ui-menu .ui-menu-item { 111 | position: relative; 112 | margin: 0; 113 | padding: 3px 1em 3px .4em; 114 | cursor: pointer; 115 | min-height: 0; /* support: IE7 */ 116 | /* support: IE10, see #8844 */ 117 | list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); 118 | } 119 | .ui-menu .ui-menu-divider { 120 | margin: 5px 0; 121 | height: 0; 122 | font-size: 0; 123 | line-height: 0; 124 | border-width: 1px 0 0 0; 125 | } 126 | .ui-menu .ui-state-focus, 127 | .ui-menu .ui-state-active { 128 | margin: -1px; 129 | } 130 | 131 | /* icon support */ 132 | .ui-menu-icons { 133 | position: relative; 134 | } 135 | .ui-menu-icons .ui-menu-item { 136 | padding-left: 2em; 137 | } 138 | 139 | /* left-aligned */ 140 | .ui-menu .ui-icon { 141 | position: absolute; 142 | top: 0; 143 | bottom: 0; 144 | left: .2em; 145 | margin: auto 0; 146 | } 147 | 148 | /* right-aligned */ 149 | .ui-menu .ui-menu-icon { 150 | left: auto; 151 | right: 0; 152 | } 153 | -------------------------------------------------------------------------------- /docs/javadoc/jquery/jquery-ui.structure.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.4 - 2015-05-20 2 | * http://jqueryui.com 3 | * Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:none}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0} 6 | -------------------------------------------------------------------------------- /docs/javadoc/jquery/jszip-utils/dist/jszip-utils-ie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZipUtils - A collection of cross-browser utilities to go along with JSZip. 4 | 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | ;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o\r\n"+ 18 | "\r\n"; 32 | 33 | // inject VBScript 34 | document.write(IEBinaryToArray_ByteStr_Script); 35 | 36 | global.JSZipUtils._getBinaryFromXHR = function (xhr) { 37 | var binary = xhr.responseBody; 38 | var byteMapping = {}; 39 | for ( var i = 0; i < 256; i++ ) { 40 | for ( var j = 0; j < 256; j++ ) { 41 | byteMapping[ String.fromCharCode( i + (j << 8) ) ] = 42 | String.fromCharCode(i) + String.fromCharCode(j); 43 | } 44 | } 45 | var rawBytes = IEBinaryToArray_ByteStr(binary); 46 | var lastChr = IEBinaryToArray_ByteStr_Last(binary); 47 | return rawBytes.replace(/[\s\S]/g, function( match ) { 48 | return byteMapping[match]; 49 | }) + lastChr; 50 | }; 51 | 52 | // enforcing Stuk's coding style 53 | // vim: set shiftwidth=4 softtabstop=4: 54 | 55 | },{}]},{},[1]) 56 | ; 57 | -------------------------------------------------------------------------------- /docs/javadoc/jquery/jszip-utils/dist/jszip-utils-ie.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZipUtils - A collection of cross-browser utilities to go along with JSZip. 4 | 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | !function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g\r\n";document.write(b),a.JSZipUtils._getBinaryFromXHR=function(a){for(var b=a.responseBody,c={},d=0;256>d;d++)for(var e=0;256>e;e++)c[String.fromCharCode(d+(e<<8))]=String.fromCharCode(d)+String.fromCharCode(e);var f=IEBinaryToArray_ByteStr(b),g=IEBinaryToArray_ByteStr_Last(b);return f.replace(/[\s\S]/g,function(a){return c[a]})+g}},{}]},{},[1]); 11 | -------------------------------------------------------------------------------- /docs/javadoc/jquery/jszip-utils/dist/jszip-utils.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZipUtils - A collection of cross-browser utilities to go along with JSZip. 4 | 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | !function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.JSZipUtils=e():"undefined"!=typeof global?global.JSZipUtils=e():"undefined"!=typeof self&&(self.JSZipUtils=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 5 | 6 | (c) 2014 Stuart Knightley, David Duponchel 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip-utils/master/LICENSE.markdown. 8 | 9 | */ 10 | !function(a){"object"==typeof exports?module.exports=a():"function"==typeof define&&define.amd?define(a):"undefined"!=typeof window?window.JSZipUtils=a():"undefined"!=typeof global?global.JSZipUtils=a():"undefined"!=typeof self&&(self.JSZipUtils=a())}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g, CompletableFuture)","url":"%3Cinit%3E(io.vertx.core.streams.ReadStream,java.util.concurrent.CompletableFuture)"},{"p":"io.reactiverse.awssdk.reactivestreams","c":"ReadStreamPublisher","l":"ReadStreamPublisher(ReadStream)","url":"%3Cinit%3E(io.vertx.core.streams.ReadStream)"},{"p":"io.reactiverse.awssdk.reactivestreams","c":"WriteStreamSubscriber","l":"stream"},{"p":"io.reactiverse.awssdk.reactivestreams","c":"ReadStreamPublisher","l":"subscribe(Subscriber)","url":"subscribe(org.reactivestreams.Subscriber)"},{"p":"io.reactiverse.awssdk","c":"VertxExecutor","l":"VertxExecutor(Context)","url":"%3Cinit%3E(io.vertx.core.Context)"},{"p":"io.reactiverse.awssdk","c":"VertxNioAsyncHttpClient","l":"VertxNioAsyncHttpClient(Context)","url":"%3Cinit%3E(io.vertx.core.Context)"},{"p":"io.reactiverse.awssdk","c":"VertxSdkClient","l":"withVertx(B, Context)","url":"withVertx(B,io.vertx.core.Context)"},{"p":"io.reactiverse.awssdk.reactivestreams","c":"WriteStreamSubscriber","l":"WriteStreamSubscriber(T)","url":"%3Cinit%3E(T)"}] -------------------------------------------------------------------------------- /docs/javadoc/member-search-index.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/member-search-index.zip -------------------------------------------------------------------------------- /docs/javadoc/overview-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | aws-sdk 0.0.1-SNAPSHOT API 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 |
17 | 20 |

index.html

21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /docs/javadoc/overview-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Class Hierarchy (aws-sdk 0.0.1-SNAPSHOT API) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 34 | 37 |
38 | 94 |
95 |
96 |
97 |

Hierarchy For All Packages

98 | Package Hierarchies: 99 | 104 |
105 |
106 |
107 |

Class Hierarchy

108 | 123 |
124 |
125 |

Interface Hierarchy

126 | 129 |
130 |
131 |
132 |
133 | 176 |
177 | 178 | 179 | -------------------------------------------------------------------------------- /docs/javadoc/package-search-index.js: -------------------------------------------------------------------------------- 1 | packageSearchIndex = [{"l":"All Packages","url":"allpackages-index.html"},{"l":"io.reactiverse.awssdk"},{"l":"io.reactiverse.awssdk.converters"},{"l":"io.reactiverse.awssdk.reactivestreams"}] -------------------------------------------------------------------------------- /docs/javadoc/package-search-index.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/package-search-index.zip -------------------------------------------------------------------------------- /docs/javadoc/resources/glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/resources/glass.png -------------------------------------------------------------------------------- /docs/javadoc/resources/x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/resources/x.png -------------------------------------------------------------------------------- /docs/javadoc/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. 3 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | */ 25 | 26 | var moduleSearchIndex; 27 | var packageSearchIndex; 28 | var typeSearchIndex; 29 | var memberSearchIndex; 30 | var tagSearchIndex; 31 | function loadScripts(doc, tag) { 32 | createElem(doc, tag, 'jquery/jszip/dist/jszip.js'); 33 | createElem(doc, tag, 'jquery/jszip-utils/dist/jszip-utils.js'); 34 | if (window.navigator.userAgent.indexOf('MSIE ') > 0 || window.navigator.userAgent.indexOf('Trident/') > 0 || 35 | window.navigator.userAgent.indexOf('Edge/') > 0) { 36 | createElem(doc, tag, 'jquery/jszip-utils/dist/jszip-utils-ie.js'); 37 | } 38 | createElem(doc, tag, 'search.js'); 39 | 40 | $.get(pathtoroot + "module-search-index.zip") 41 | .done(function() { 42 | JSZipUtils.getBinaryContent(pathtoroot + "module-search-index.zip", function(e, data) { 43 | var zip = new JSZip(data); 44 | zip.load(data); 45 | moduleSearchIndex = JSON.parse(zip.file("module-search-index.json").asText()); 46 | }); 47 | }); 48 | $.get(pathtoroot + "package-search-index.zip") 49 | .done(function() { 50 | JSZipUtils.getBinaryContent(pathtoroot + "package-search-index.zip", function(e, data) { 51 | var zip = new JSZip(data); 52 | zip.load(data); 53 | packageSearchIndex = JSON.parse(zip.file("package-search-index.json").asText()); 54 | }); 55 | }); 56 | $.get(pathtoroot + "type-search-index.zip") 57 | .done(function() { 58 | JSZipUtils.getBinaryContent(pathtoroot + "type-search-index.zip", function(e, data) { 59 | var zip = new JSZip(data); 60 | zip.load(data); 61 | typeSearchIndex = JSON.parse(zip.file("type-search-index.json").asText()); 62 | }); 63 | }); 64 | $.get(pathtoroot + "member-search-index.zip") 65 | .done(function() { 66 | JSZipUtils.getBinaryContent(pathtoroot + "member-search-index.zip", function(e, data) { 67 | var zip = new JSZip(data); 68 | zip.load(data); 69 | memberSearchIndex = JSON.parse(zip.file("member-search-index.json").asText()); 70 | }); 71 | }); 72 | $.get(pathtoroot + "tag-search-index.zip") 73 | .done(function() { 74 | JSZipUtils.getBinaryContent(pathtoroot + "tag-search-index.zip", function(e, data) { 75 | var zip = new JSZip(data); 76 | zip.load(data); 77 | tagSearchIndex = JSON.parse(zip.file("tag-search-index.json").asText()); 78 | }); 79 | }); 80 | if (!moduleSearchIndex) { 81 | createElem(doc, tag, 'module-search-index.js'); 82 | } 83 | if (!packageSearchIndex) { 84 | createElem(doc, tag, 'package-search-index.js'); 85 | } 86 | if (!typeSearchIndex) { 87 | createElem(doc, tag, 'type-search-index.js'); 88 | } 89 | if (!memberSearchIndex) { 90 | createElem(doc, tag, 'member-search-index.js'); 91 | } 92 | if (!tagSearchIndex) { 93 | createElem(doc, tag, 'tag-search-index.js'); 94 | } 95 | $(window).resize(function() { 96 | $('.navPadding').css('padding-top', $('.fixedNav').css("height")); 97 | }); 98 | } 99 | 100 | function createElem(doc, tag, path) { 101 | var script = doc.createElement(tag); 102 | var scriptElement = doc.getElementsByTagName(tag)[0]; 103 | script.src = pathtoroot + path; 104 | scriptElement.parentNode.insertBefore(script, scriptElement); 105 | } 106 | 107 | function show(type) { 108 | count = 0; 109 | for (var key in data) { 110 | var row = document.getElementById(key); 111 | if ((data[key] & type) !== 0) { 112 | row.style.display = ''; 113 | row.className = (count++ % 2) ? rowColor : altColor; 114 | } 115 | else 116 | row.style.display = 'none'; 117 | } 118 | updateTabs(type); 119 | } 120 | 121 | function updateTabs(type) { 122 | for (var value in tabs) { 123 | var sNode = document.getElementById(tabs[value][0]); 124 | var spanNode = sNode.firstChild; 125 | if (value == type) { 126 | sNode.className = activeTableTab; 127 | spanNode.innerHTML = tabs[value][1]; 128 | } 129 | else { 130 | sNode.className = tableTab; 131 | spanNode.innerHTML = "" + tabs[value][1] + ""; 132 | } 133 | } 134 | } 135 | 136 | function updateModuleFrame(pFrame, cFrame) { 137 | top.packageFrame.location = pFrame; 138 | top.classFrame.location = cFrame; 139 | } 140 | -------------------------------------------------------------------------------- /docs/javadoc/type-search-index.js: -------------------------------------------------------------------------------- 1 | typeSearchIndex = [{"l":"All Classes","url":"allclasses-index.html"},{"p":"io.reactiverse.awssdk.reactivestreams","l":"HttpClientRequestSubscriber"},{"p":"io.reactiverse.awssdk.converters","l":"MethodConverter"},{"p":"io.reactiverse.awssdk.reactivestreams","l":"ReadStreamPublisher"},{"p":"io.reactiverse.awssdk","l":"VertxExecutor"},{"p":"io.reactiverse.awssdk","l":"VertxNioAsyncHttpClient"},{"p":"io.reactiverse.awssdk","l":"VertxSdkClient"},{"p":"io.reactiverse.awssdk.reactivestreams","l":"WriteStreamSubscriber"}] -------------------------------------------------------------------------------- /docs/javadoc/type-search-index.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/docs/javadoc/type-search-index.zip -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4096m 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'aws-sdk' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/io/reactiverse/awssdk/VertxExecutor.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk; 2 | 3 | import io.vertx.core.Context; 4 | 5 | import java.util.concurrent.Executor; 6 | 7 | /** 8 | * Vertx executor that runs the specified command in the current context. 9 | * Can only work if the runnable is non-blocking 10 | */ 11 | public class VertxExecutor implements Executor { 12 | 13 | private Context context; 14 | 15 | public VertxExecutor(Context context) { 16 | this.context = context; 17 | } 18 | 19 | 20 | @Override 21 | public void execute(Runnable command) { 22 | context.runOnContext(v -> command.run()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/reactiverse/awssdk/VertxNioAsyncHttpClient.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk; 2 | 3 | import io.reactiverse.awssdk.converters.MethodConverter; 4 | import io.reactiverse.awssdk.reactivestreams.HttpClientRequestSubscriber; 5 | import io.reactiverse.awssdk.reactivestreams.ReadStreamPublisher; 6 | import io.vertx.core.Context; 7 | import io.vertx.core.Vertx; 8 | import io.vertx.core.http.HttpClient; 9 | import io.vertx.core.http.HttpClientOptions; 10 | import io.vertx.core.http.HttpClientRequest; 11 | import io.vertx.core.http.HttpClientResponse; 12 | import io.vertx.core.http.HttpHeaders; 13 | import io.vertx.core.http.HttpMethod; 14 | import io.vertx.core.http.RequestOptions; 15 | import software.amazon.awssdk.http.SdkHttpFullResponse; 16 | import software.amazon.awssdk.http.SdkHttpRequest; 17 | import software.amazon.awssdk.http.SdkHttpResponse; 18 | import software.amazon.awssdk.http.async.AsyncExecuteRequest; 19 | import software.amazon.awssdk.http.async.SdkAsyncHttpClient; 20 | import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; 21 | import software.amazon.awssdk.http.async.SdkHttpContentPublisher; 22 | import software.amazon.awssdk.utils.StringUtils; 23 | 24 | import java.net.URI; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | import java.util.concurrent.CompletableFuture; 28 | import java.util.stream.Collectors; 29 | 30 | import static java.util.Objects.requireNonNull; 31 | 32 | public class VertxNioAsyncHttpClient implements SdkAsyncHttpClient { 33 | 34 | private final Context context; 35 | private final HttpClient client; 36 | private final HttpClientOptions clientOptions; 37 | 38 | private static final HttpClientOptions DEFAULT_CLIENT_OPTIONS = new HttpClientOptions() 39 | .setSsl(true) 40 | .setKeepAlive(true); 41 | 42 | public VertxNioAsyncHttpClient(Context context) { 43 | this.context = context; 44 | this.clientOptions = DEFAULT_CLIENT_OPTIONS; 45 | this.client = createVertxHttpClient(context.owner()); 46 | } 47 | 48 | public VertxNioAsyncHttpClient(Context context, HttpClientOptions clientOptions) { 49 | requireNonNull(clientOptions); 50 | this.context = context; 51 | this.clientOptions = clientOptions; 52 | this.client = createVertxHttpClient(context.owner()); 53 | } 54 | 55 | private HttpClient createVertxHttpClient(Vertx vertx) { 56 | return vertx.createHttpClient(clientOptions); 57 | } 58 | 59 | @Override 60 | public CompletableFuture execute(AsyncExecuteRequest asyncExecuteRequest) { 61 | final CompletableFuture fut = new CompletableFuture<>(); 62 | if (Context.isOnEventLoopThread()) { 63 | executeOnContext(asyncExecuteRequest, fut); 64 | } else { 65 | context.runOnContext(v -> executeOnContext(asyncExecuteRequest, fut)); 66 | } 67 | return fut; 68 | } 69 | 70 | void executeOnContext(AsyncExecuteRequest asyncExecuteRequest, CompletableFuture fut) { 71 | final SdkHttpRequest request = asyncExecuteRequest.request(); 72 | final SdkAsyncHttpResponseHandler responseHandler = asyncExecuteRequest.responseHandler(); 73 | final RequestOptions options = getRequestOptions(request); 74 | client.request(options, ar -> { 75 | if (ar.failed()) { 76 | responseHandler.onError(ar.cause()); 77 | return; 78 | } 79 | HttpClientRequest vRequest = ar.result(); 80 | vRequest.response(res -> { 81 | if (res.failed()) { 82 | responseHandler.onError(res.cause()); 83 | fut.completeExceptionally(res.cause()); 84 | return; 85 | } 86 | HttpClientResponse vResponse = res.result(); 87 | final SdkHttpFullResponse.Builder builder = SdkHttpResponse.builder() 88 | .statusCode(vResponse.statusCode()) 89 | .statusText(vResponse.statusMessage()); 90 | vResponse.headers().forEach(e -> 91 | builder.appendHeader(e.getKey(), e.getValue()) 92 | ); 93 | responseHandler.onHeaders(builder.build()); 94 | responseHandler.onStream(new ReadStreamPublisher<>(vResponse, fut)); 95 | }); 96 | final SdkHttpContentPublisher publisher = asyncExecuteRequest.requestContentPublisher(); 97 | if (publisher != null) { 98 | publisher.subscribe(new HttpClientRequestSubscriber(vRequest)); 99 | } else { 100 | vRequest.end(); 101 | } 102 | }); 103 | } 104 | 105 | private static RequestOptions getRequestOptions(SdkHttpRequest request) { 106 | RequestOptions options = new RequestOptions() 107 | .setMethod(MethodConverter.awsToVertx(request.method())) 108 | .setHost(request.host()) 109 | .setPort(request.port()) 110 | .setURI(createRelativeUri(request.getUri())) 111 | .setFollowRedirects(true) 112 | .setSsl("https".equals(request.protocol())); 113 | request.headers().forEach((name, values) -> { 114 | options.addHeader(name, values.stream().map(s -> (CharSequence)s).collect(Collectors.toList())); 115 | }); 116 | options.addHeader(HttpHeaders.CONNECTION, HttpHeaders.KEEP_ALIVE); 117 | return options; 118 | } 119 | 120 | private static String createRelativeUri(URI uri) { 121 | return (StringUtils.isEmpty(uri.getPath()) ? "/" : uri.getPath()) + 122 | // AWS requires query parameters to be encoded as defined by RFC 3986. 123 | // see https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html 124 | // uri.toASCIIString() returns the URI encoded in this manner 125 | (StringUtils.isEmpty(uri.getQuery()) ? "" : "?" + uri.toASCIIString().split("\\?")[1]); 126 | } 127 | 128 | @Override 129 | public void close() { 130 | client.close(); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/io/reactiverse/awssdk/VertxSdkClient.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk; 2 | 3 | import io.vertx.core.Context; 4 | import io.vertx.core.http.HttpClientOptions; 5 | import software.amazon.awssdk.awscore.client.builder.AwsAsyncClientBuilder; 6 | import software.amazon.awssdk.core.SdkClient; 7 | import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption; 8 | 9 | public interface VertxSdkClient { 10 | 11 | static> B withVertx(B builder, Context context) { 12 | return builder 13 | .httpClient(new VertxNioAsyncHttpClient(context)) 14 | .asyncConfiguration(conf -> 15 | conf.advancedOption(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, new VertxExecutor(context)) 16 | ); 17 | } 18 | 19 | static> B withVertx(B builder, HttpClientOptions clientOptions, Context context) { 20 | return builder 21 | .httpClient(new VertxNioAsyncHttpClient(context, clientOptions)) 22 | .asyncConfiguration(conf -> 23 | conf.advancedOption(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, new VertxExecutor(context)) 24 | ); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/reactiverse/awssdk/converters/MethodConverter.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.converters; 2 | 3 | import io.vertx.core.http.HttpMethod; 4 | import software.amazon.awssdk.http.SdkHttpMethod; 5 | 6 | import java.util.EnumMap; 7 | import java.util.Map; 8 | 9 | public final class MethodConverter { 10 | 11 | private MethodConverter() {} 12 | private static Map sdkToVertx = new EnumMap<>(SdkHttpMethod.class); 13 | static { 14 | sdkToVertx.put(SdkHttpMethod.GET, HttpMethod.GET); 15 | sdkToVertx.put(SdkHttpMethod.POST, HttpMethod.POST); 16 | sdkToVertx.put(SdkHttpMethod.PUT, HttpMethod.PUT); 17 | sdkToVertx.put(SdkHttpMethod.DELETE, HttpMethod.DELETE); 18 | sdkToVertx.put(SdkHttpMethod.HEAD, HttpMethod.HEAD); 19 | sdkToVertx.put(SdkHttpMethod.PATCH, HttpMethod.PATCH); 20 | sdkToVertx.put(SdkHttpMethod.OPTIONS, HttpMethod.OPTIONS); 21 | } 22 | 23 | public static HttpMethod awsToVertx(SdkHttpMethod method) { 24 | return sdkToVertx.get(method); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/reactiverse/awssdk/converters/VertxAsyncResponseTransformer.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.converters; 2 | 3 | import io.reactiverse.awssdk.reactivestreams.WriteStreamSubscriber; 4 | import io.vertx.core.Handler; 5 | import io.vertx.core.buffer.Buffer; 6 | import io.vertx.core.streams.WriteStream; 7 | import software.amazon.awssdk.core.async.AsyncResponseTransformer; 8 | import software.amazon.awssdk.core.async.SdkPublisher; 9 | 10 | import java.nio.ByteBuffer; 11 | import java.util.Optional; 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | public class VertxAsyncResponseTransformer implements AsyncResponseTransformer> { 15 | 16 | private volatile CompletableFuture> cf; 17 | private volatile WriteStream writeStream; 18 | private volatile Optional> responseHandler; 19 | 20 | public VertxAsyncResponseTransformer(WriteStream ws) { 21 | this.writeStream = ws; 22 | responseHandler = Optional.empty(); 23 | } 24 | 25 | @Override 26 | public CompletableFuture> prepare() { 27 | cf = new CompletableFuture<>(); 28 | return cf; 29 | } 30 | 31 | @Override 32 | public void onResponse(ResponseT response) { 33 | this.responseHandler.ifPresent(handler -> handler.handle(response)); 34 | } 35 | 36 | @Override 37 | public void onStream(SdkPublisher publisher) { 38 | publisher.subscribe(new WriteStreamSubscriber<>(writeStream, cf)); 39 | } 40 | 41 | @Override 42 | public void exceptionOccurred(Throwable error) { 43 | cf.completeExceptionally(error); 44 | } 45 | 46 | public VertxAsyncResponseTransformer setResponseHandler(Handler handler) { 47 | this.responseHandler = Optional.of(handler); 48 | return this; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/reactiverse/awssdk/reactivestreams/HttpClientRequestSubscriber.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.reactivestreams; 2 | 3 | import io.vertx.core.http.HttpClientRequest; 4 | import io.vertx.core.http.HttpHeaders; 5 | 6 | import java.nio.ByteBuffer; 7 | 8 | public class HttpClientRequestSubscriber extends WriteStreamSubscriber { 9 | 10 | public HttpClientRequestSubscriber(HttpClientRequest request) { 11 | super(request); 12 | } 13 | 14 | @Override 15 | public void onNext(ByteBuffer byteBuffer) { 16 | if (!stream.isChunked() && !stream.headers().contains(HttpHeaders.CONTENT_LENGTH) && byteBuffer.array().length != 0) { 17 | stream.setChunked(true); 18 | } 19 | super.onNext(byteBuffer); 20 | } 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/io/reactiverse/awssdk/reactivestreams/ReadStreamPublisher.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.reactivestreams; 2 | 3 | import io.vertx.core.buffer.Buffer; 4 | import io.vertx.core.streams.ReadStream; 5 | import org.reactivestreams.Publisher; 6 | import org.reactivestreams.Subscriber; 7 | import org.reactivestreams.Subscription; 8 | 9 | import java.nio.ByteBuffer; 10 | import java.util.concurrent.CompletableFuture; 11 | 12 | public class ReadStreamPublisher implements Publisher { 13 | 14 | private ReadStream stream; 15 | private CompletableFuture future; 16 | 17 | public ReadStreamPublisher(ReadStream readStream) { 18 | this(readStream, null); 19 | } 20 | 21 | public ReadStreamPublisher(ReadStream readStream, CompletableFuture future) { 22 | this.stream = readStream; 23 | this.future = future; 24 | } 25 | 26 | @Override 27 | public void subscribe(Subscriber s) { 28 | s.onSubscribe(new Subscription() { 29 | @Override 30 | public void request(long n) { 31 | stream.fetch(n); 32 | } 33 | 34 | @Override 35 | public void cancel() { 36 | // Cannot really do anything on the stream 37 | // stream.pause() maybe ? 38 | } 39 | }); 40 | stream.endHandler(v -> { 41 | s.onComplete(); 42 | if (future != null) { 43 | future.complete(null); 44 | } 45 | }); 46 | stream.handler(buff -> 47 | s.onNext(ByteBuffer.wrap(buff.getBytes())) 48 | ); 49 | stream.exceptionHandler(s::onError); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/reactiverse/awssdk/reactivestreams/WriteStreamSubscriber.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.reactivestreams; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import io.vertx.core.buffer.Buffer; 5 | import io.vertx.core.streams.WriteStream; 6 | import org.reactivestreams.Subscriber; 7 | import org.reactivestreams.Subscription; 8 | 9 | import java.nio.ByteBuffer; 10 | import java.util.Optional; 11 | import java.util.concurrent.CompletableFuture; 12 | 13 | public class WriteStreamSubscriber> implements Subscriber { 14 | 15 | protected static final long BUFF_SIZE = 1024; 16 | 17 | protected T stream; 18 | private Subscription subscription; 19 | private Optional>> cf; 20 | 21 | public WriteStreamSubscriber(T stream) { 22 | this.stream = stream; 23 | cf = Optional.empty(); 24 | } 25 | 26 | public WriteStreamSubscriber(T stream, CompletableFuture> cf) { 27 | this.stream = stream; 28 | this.cf = Optional.of(cf); 29 | } 30 | 31 | @Override 32 | public void onSubscribe(Subscription subscription) { 33 | this.subscription = subscription; 34 | subscription.request(BUFF_SIZE); 35 | } 36 | 37 | @Override 38 | public void onNext(ByteBuffer byteBuffer) { 39 | if (byteBuffer.hasRemaining()) { 40 | Buffer buffer = Buffer.buffer(Unpooled.wrappedBuffer(byteBuffer)); 41 | stream.write(buffer); 42 | } 43 | subscription.request(BUFF_SIZE); 44 | } 45 | 46 | @Override 47 | public void onError(Throwable t) { 48 | subscription.cancel(); 49 | } 50 | 51 | 52 | @Override 53 | public void onComplete() { 54 | stream.end(); 55 | cf.map(fut -> fut.complete(stream)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/AsyncHttpClientTest.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import io.vertx.core.Vertx; 5 | import io.vertx.core.http.HttpServer; 6 | import io.vertx.junit5.Timeout; 7 | import io.vertx.junit5.VertxExtension; 8 | import io.vertx.junit5.VertxTestContext; 9 | import org.junit.jupiter.api.AfterEach; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.junit.jupiter.api.extension.ExtendWith; 13 | import org.reactivestreams.Publisher; 14 | import software.amazon.awssdk.core.internal.http.async.SimpleHttpContentPublisher; 15 | import software.amazon.awssdk.http.SdkHttpFullRequest; 16 | import software.amazon.awssdk.http.SdkHttpMethod; 17 | import software.amazon.awssdk.http.SdkHttpRequest; 18 | import software.amazon.awssdk.http.SdkHttpResponse; 19 | import software.amazon.awssdk.http.async.AsyncExecuteRequest; 20 | import software.amazon.awssdk.http.async.SdkAsyncHttpClient; 21 | import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; 22 | import software.amazon.awssdk.http.async.SimpleSubscriber; 23 | 24 | import java.io.ByteArrayInputStream; 25 | import java.nio.ByteBuffer; 26 | import java.nio.charset.StandardCharsets; 27 | import java.util.concurrent.TimeUnit; 28 | 29 | import static org.junit.jupiter.api.Assertions.assertEquals; 30 | import static org.junit.jupiter.api.Assertions.assertTrue; 31 | 32 | @ExtendWith(VertxExtension.class) 33 | public class AsyncHttpClientTest { 34 | 35 | private Vertx vertx; 36 | private HttpServer server; 37 | private SdkAsyncHttpClient client; 38 | 39 | private static final int PORT = 8000; 40 | private static final String HOST = "localhost"; 41 | private static final String SCHEME = "http"; 42 | 43 | @BeforeEach 44 | public void setUp() { 45 | vertx = Vertx.vertx(); 46 | server = vertx.createHttpServer(); 47 | client = new VertxNioAsyncHttpClient(vertx.getOrCreateContext()); 48 | } 49 | 50 | @AfterEach 51 | public void tearDown(VertxTestContext ctx) { 52 | if (server == null) { 53 | return; 54 | } 55 | server.close(res -> { 56 | assertTrue(res.succeeded()); 57 | ctx.completeNow(); 58 | }); 59 | } 60 | 61 | @Test 62 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 63 | public void testGet(VertxTestContext ctx) { 64 | server.requestHandler(req -> { 65 | req.response().end("foo"); 66 | }); 67 | server.listen(PORT, HOST, res -> { 68 | assertTrue(res.succeeded()); 69 | client.execute(AsyncExecuteRequest.builder() 70 | .request(SdkHttpRequest 71 | .builder() 72 | .protocol(SCHEME) 73 | .host(HOST) 74 | .port(PORT) 75 | .method(SdkHttpMethod.GET) 76 | .build()) 77 | .responseHandler(new SdkAsyncHttpResponseHandler() { 78 | @Override 79 | public void onHeaders(SdkHttpResponse headers) { 80 | assertEquals(200, headers.statusCode()); 81 | } 82 | @Override 83 | public void onStream(Publisher stream) { 84 | stream.subscribe(new SimpleSubscriber(body -> { 85 | assertEquals("foo", Unpooled.wrappedBuffer(body).toString(StandardCharsets.UTF_8)); 86 | ctx.completeNow(); 87 | })); 88 | } 89 | @Override 90 | public void onError(Throwable error) { 91 | throw new RuntimeException(error); 92 | } 93 | }) 94 | .build()); 95 | }); 96 | } 97 | 98 | @Test 99 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 100 | public void testPut(VertxTestContext ctx) { 101 | final byte[] payload = "the-body".getBytes(); 102 | 103 | server.requestHandler(req -> { 104 | req.bodyHandler(buff -> { 105 | req.response().end(buff); 106 | }); 107 | }); 108 | server.listen(PORT, HOST, res -> { 109 | assertTrue(res.succeeded()); 110 | SdkHttpFullRequest request = SdkHttpFullRequest 111 | .builder() 112 | .protocol(SCHEME) 113 | .host(HOST) 114 | .port(PORT) 115 | .method(SdkHttpMethod.PUT) 116 | .putHeader("Content-Length", String.valueOf(payload.length)) 117 | .contentStreamProvider(() -> new ByteArrayInputStream(payload)) 118 | .build(); 119 | client.execute(AsyncExecuteRequest.builder() 120 | .request(request) 121 | .requestContentPublisher(new SimpleHttpContentPublisher(request)) 122 | .responseHandler(new SdkAsyncHttpResponseHandler() { 123 | @Override 124 | public void onHeaders(SdkHttpResponse headers) { 125 | assertEquals(200, headers.statusCode()); 126 | } 127 | 128 | @Override 129 | public void onStream(Publisher stream) { 130 | stream.subscribe(new SimpleSubscriber(body -> { 131 | assertEquals("the-body", Unpooled.wrappedBuffer(body).toString(StandardCharsets.UTF_8)); 132 | ctx.completeNow(); 133 | })); 134 | } 135 | 136 | @Override 137 | public void onError(Throwable error) { 138 | throw new RuntimeException(error); 139 | } 140 | }) 141 | .build()); 142 | }); 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/ContextAssertVertxNioAsyncHttpClient.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk; 2 | 3 | import io.vertx.core.Context; 4 | import io.vertx.core.Vertx; 5 | import software.amazon.awssdk.http.async.AsyncExecuteRequest; 6 | 7 | import java.util.concurrent.CompletableFuture; 8 | 9 | public class ContextAssertVertxNioAsyncHttpClient extends VertxNioAsyncHttpClient { 10 | 11 | private Vertx vertx; 12 | private Context creationContext; 13 | 14 | public ContextAssertVertxNioAsyncHttpClient(Vertx vertx, Context context) { 15 | super(context); 16 | this.vertx = vertx; 17 | this.creationContext = context; 18 | } 19 | 20 | @Override 21 | void executeOnContext(AsyncExecuteRequest asyncExecuteRequest, CompletableFuture fut) { 22 | if (vertx.getOrCreateContext() != this.creationContext) { 23 | throw new AssertionError("Context should ALWAYS be the same"); 24 | } 25 | super.executeOnContext(asyncExecuteRequest, fut); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/ProxyTest.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.http.HttpClientOptions; 5 | import io.vertx.core.http.HttpServer; 6 | import io.vertx.core.net.ProxyOptions; 7 | import io.vertx.junit5.Timeout; 8 | import io.vertx.junit5.VertxExtension; 9 | import io.vertx.junit5.VertxTestContext; 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import org.junit.jupiter.api.extension.ExtendWith; 14 | import software.amazon.awssdk.auth.credentials.AwsCredentials; 15 | import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; 16 | import software.amazon.awssdk.regions.Region; 17 | import software.amazon.awssdk.services.kinesis.KinesisAsyncClient; 18 | import software.amazon.awssdk.services.kinesis.KinesisAsyncClientBuilder; 19 | import software.amazon.awssdk.services.kinesis.model.ListStreamsResponse; 20 | 21 | import java.net.URI; 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.concurrent.TimeUnit; 24 | import java.util.concurrent.atomic.AtomicInteger; 25 | 26 | import static org.junit.jupiter.api.Assertions.assertEquals; 27 | import static org.junit.jupiter.api.Assertions.assertTrue; 28 | 29 | @ExtendWith(VertxExtension.class) 30 | public class ProxyTest { 31 | 32 | private Vertx vertx; 33 | private HttpServer proxyServer; 34 | 35 | private static final int PROXY_PORT = 8000; 36 | private static final String PROXY_HOST = "localhost"; 37 | 38 | private final AtomicInteger proxyAccess = new AtomicInteger(0); 39 | private static final AwsCredentialsProvider credentialsProvider = () -> new AwsCredentials() { 40 | @Override 41 | public String accessKeyId() { 42 | return "a"; 43 | } 44 | 45 | @Override 46 | public String secretAccessKey() { 47 | return "a"; 48 | } 49 | }; 50 | 51 | @BeforeEach 52 | public void setUp(VertxTestContext ctx) { 53 | vertx = Vertx.vertx(); 54 | proxyServer = vertx.createHttpServer(); 55 | proxyServer.requestHandler(req -> { 56 | proxyAccess.incrementAndGet(); 57 | req.response().end(); 58 | }); 59 | proxyServer.listen(PROXY_PORT, PROXY_HOST, res -> { 60 | assertTrue(res.succeeded()); 61 | ctx.completeNow(); 62 | }); 63 | } 64 | 65 | @AfterEach 66 | public void tearDown(VertxTestContext ctx) { 67 | if (proxyServer == null) { 68 | return; 69 | } 70 | proxyAccess.set(0); 71 | proxyServer.close(res -> { 72 | assertTrue(res.succeeded()); 73 | ctx.completeNow(); 74 | }); 75 | } 76 | 77 | @Test 78 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 79 | public void testGetThroughProxy(VertxTestContext ctx) throws Exception { 80 | final KinesisAsyncClientBuilder builder = KinesisAsyncClient.builder() 81 | .region(Region.EU_WEST_1) 82 | .endpointOverride(new URI("http://localhost:1111")) // something that just doesn't exist, the only thing that matters is that every request has traveled through proxy 83 | .credentialsProvider(credentialsProvider); 84 | HttpClientOptions throughProxyOptions = new HttpClientOptions().setProxyOptions(new ProxyOptions().setHost(PROXY_HOST).setPort(PROXY_PORT)); 85 | KinesisAsyncClient kinesis = VertxSdkClient.withVertx(builder, throughProxyOptions, vertx.getOrCreateContext()).build(); 86 | assertEquals(proxyAccess.get(), 0, "Proxy access count should have been reset"); 87 | kinesis 88 | .listStreams() 89 | .handle((res, err) -> { 90 | assertTrue(proxyAccess.get() > 0, "Requests should have been transferred through proxy"); 91 | ctx.completeNow(); 92 | return null; 93 | }); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/RetryContextTest.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk; 2 | 3 | import io.vertx.core.Context; 4 | import io.vertx.core.Vertx; 5 | import io.vertx.core.http.HttpServerOptions; 6 | import io.vertx.core.json.JsonObject; 7 | import io.vertx.junit5.Timeout; 8 | import io.vertx.junit5.VertxExtension; 9 | import io.vertx.junit5.VertxTestContext; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.junit.jupiter.api.extension.ExtendWith; 13 | import software.amazon.awssdk.auth.credentials.AwsCredentials; 14 | import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; 15 | import software.amazon.awssdk.regions.Region; 16 | import software.amazon.awssdk.services.lambda.LambdaAsyncClient; 17 | import software.amazon.awssdk.services.lambda.LambdaAsyncClientBuilder; 18 | 19 | import java.net.URI; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | import static org.junit.jupiter.api.Assertions.assertSame; 23 | 24 | 25 | @ExtendWith(VertxExtension.class) 26 | class RetryContextTest { 27 | 28 | private static final int PORT = 9876; 29 | private static final String HOST = "localhost"; 30 | private static final HttpServerOptions SERVER_OPTS = new HttpServerOptions() 31 | .setHost(HOST) 32 | .setPort(PORT); 33 | 34 | private static final AwsCredentialsProvider credentialsProvider = () -> new AwsCredentials() { 35 | @Override 36 | public String accessKeyId() { 37 | return "a"; 38 | } 39 | 40 | @Override 41 | public String secretAccessKey() { 42 | return "a"; 43 | } 44 | }; 45 | 46 | private int attempts = 0; 47 | 48 | @BeforeEach 49 | @Timeout(value = 5, timeUnit = TimeUnit.SECONDS) 50 | void setupFailServer(Vertx vertx, VertxTestContext ctx) { 51 | attempts = 0; 52 | vertx.createHttpServer(SERVER_OPTS) 53 | .requestHandler(req -> { 54 | attempts++; 55 | if (attempts == 1) { // only fail for first attempt, forcing a retry 56 | req.response().setStatusCode(500).end(); 57 | } else { 58 | req.response().setStatusCode(200).end(new JsonObject().encode()); 59 | } 60 | 61 | }) 62 | .listen(ctx.succeedingThenComplete()); 63 | } 64 | 65 | @Test 66 | @Timeout(value = 20, timeUnit = TimeUnit.SECONDS) 67 | void testRetry(Vertx vertx, VertxTestContext ctx) throws Exception { 68 | Context originalContext = vertx.getOrCreateContext(); 69 | createLambdaClient(vertx, originalContext) 70 | .getFunction(gfb -> gfb.functionName("dummy")) 71 | .whenComplete((resp, error) -> { 72 | if (error != null) { 73 | ctx.failNow(error); 74 | } else { 75 | ctx.verify(() -> { 76 | assertSame(originalContext, vertx.getOrCreateContext()); 77 | ctx.completeNow(); 78 | }); 79 | 80 | } 81 | }); 82 | } 83 | 84 | LambdaAsyncClient createLambdaClient(Vertx vertx, Context ctx) throws Exception { 85 | final URI lambdaURI = new URI("http://" + HOST + ":" + PORT); 86 | final LambdaAsyncClientBuilder builder = 87 | LambdaAsyncClient.builder() 88 | .credentialsProvider(credentialsProvider) 89 | .region(Region.EU_WEST_1) 90 | .endpointOverride(lambdaURI); 91 | return VertxSdkClient 92 | .withVertx(builder, ctx) 93 | .httpClient(new ContextAssertVertxNioAsyncHttpClient(vertx, ctx)) // override on purpose, we look forward to assert that the context is always the same 94 | .build(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/integration/LocalStackBaseSpec.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.integration; 2 | 3 | import cloud.localstack.Localstack; 4 | import io.reactivex.Single; 5 | import io.reactivex.SingleOnSubscribe; 6 | import io.vertx.core.Context; 7 | import io.vertx.core.Vertx; 8 | import io.vertx.junit5.VertxTestContext; 9 | import software.amazon.awssdk.auth.credentials.AwsCredentials; 10 | import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; 11 | import software.amazon.awssdk.regions.Region; 12 | import software.amazon.awssdk.services.s3.S3AsyncClient; 13 | import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; 14 | 15 | import java.net.URI; 16 | import java.util.concurrent.CompletableFuture; 17 | 18 | import static io.reactiverse.awssdk.VertxSdkClient.withVertx; 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | 21 | public abstract class LocalStackBaseSpec { 22 | 23 | protected static final AwsCredentialsProvider credentialsProvider = () -> new AwsCredentials() { 24 | @Override 25 | public String accessKeyId() { 26 | return "a"; 27 | } 28 | 29 | @Override 30 | public String secretAccessKey() { 31 | return "a"; 32 | } 33 | }; 34 | 35 | protected void assertContext(Vertx vertx, Context context, VertxTestContext testContext) { 36 | testContext.verify(() -> { 37 | assertEquals(context, vertx.getOrCreateContext()); 38 | }); 39 | } 40 | 41 | protected static URI s3URI() throws Exception { 42 | return new URI(Localstack.INSTANCE.getEndpointS3()); 43 | } 44 | 45 | protected static S3AsyncClient s3(Context context) throws Exception { 46 | final S3AsyncClientBuilder builder = S3AsyncClient.builder() 47 | .serviceConfiguration(sc -> 48 | sc.checksumValidationEnabled(false) 49 | .pathStyleAccessEnabled(true) // from localstack documentation 50 | ) 51 | .credentialsProvider(credentialsProvider) 52 | .endpointOverride(s3URI()) 53 | .region(Region.EU_WEST_1); 54 | return withVertx(builder, context).build(); 55 | } 56 | 57 | 58 | protected static Single single(CompletableFuture future) { 59 | final SingleOnSubscribe sos = emitter -> 60 | future.handle((result, error) -> { 61 | if (error != null) { 62 | emitter.onError(error); 63 | } else { 64 | emitter.onSuccess(result); 65 | } 66 | return future; 67 | }); 68 | return Single.create(sos); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/integration/apigateway/VertxApiGatewaySpec.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.integration.apigateway; 2 | 3 | import cloud.localstack.Localstack; 4 | import cloud.localstack.ServiceName; 5 | import cloud.localstack.docker.LocalstackDockerExtension; 6 | import cloud.localstack.docker.annotation.LocalstackDockerProperties; 7 | import io.reactiverse.awssdk.VertxSdkClient; 8 | import io.reactiverse.awssdk.integration.LocalStackBaseSpec; 9 | import io.reactivex.Single; 10 | import io.vertx.core.Context; 11 | import io.vertx.core.Vertx; 12 | import io.vertx.core.http.HttpMethod; 13 | import io.vertx.junit5.Timeout; 14 | import io.vertx.junit5.VertxExtension; 15 | import io.vertx.junit5.VertxTestContext; 16 | import org.junit.jupiter.api.Test; 17 | import org.junit.jupiter.api.condition.EnabledIfSystemProperty; 18 | import org.junit.jupiter.api.extension.ExtendWith; 19 | import software.amazon.awssdk.http.HttpStatusCode; 20 | import software.amazon.awssdk.regions.Region; 21 | import software.amazon.awssdk.services.apigateway.ApiGatewayAsyncClient; 22 | import software.amazon.awssdk.services.apigateway.ApiGatewayAsyncClientBuilder; 23 | import software.amazon.awssdk.services.apigateway.model.CreateResourceResponse; 24 | import software.amazon.awssdk.services.apigateway.model.CreateRestApiRequest; 25 | import software.amazon.awssdk.services.apigateway.model.CreateRestApiResponse; 26 | import software.amazon.awssdk.services.apigateway.model.GetResourcesResponse; 27 | import software.amazon.awssdk.services.apigateway.model.IntegrationType; 28 | import software.amazon.awssdk.services.apigateway.model.PutIntegrationResponse; 29 | import software.amazon.awssdk.services.apigateway.model.PutIntegrationResponseResponse; 30 | import software.amazon.awssdk.services.apigateway.model.PutMethodResponse; 31 | import software.amazon.awssdk.services.apigateway.model.PutMethodResponseResponse; 32 | import software.amazon.awssdk.services.apigateway.model.Resource; 33 | import software.amazon.awssdk.services.apigateway.model.TestInvokeMethodResponse; 34 | 35 | import java.net.URI; 36 | import java.util.HashMap; 37 | import java.util.List; 38 | import java.util.Map; 39 | import java.util.concurrent.TimeUnit; 40 | 41 | import static org.junit.jupiter.api.Assertions.assertEquals; 42 | 43 | @EnabledIfSystemProperty(named = "tests.integration", matches = "localstack") 44 | @LocalstackDockerProperties(services = { ServiceName.API_GATEWAY }, imageTag = "1.4.0") 45 | @ExtendWith(VertxExtension.class) 46 | @ExtendWith(LocalstackDockerExtension.class) 47 | public class VertxApiGatewaySpec extends LocalStackBaseSpec { 48 | 49 | private final static String API_NAME = "MyAPI"; 50 | private final static String PATH = "faking"; 51 | private final static HttpMethod METHOD = HttpMethod.GET; 52 | private final static Map TEMPLATES = new HashMap<>(); 53 | private final static String MOCK_RESPONSE = "{\"message\": \"Hello from a fake backend\"}"; 54 | static { 55 | TEMPLATES.put("application/json", MOCK_RESPONSE); 56 | } 57 | 58 | private ApiGatewayAsyncClient gatewayClient; 59 | private String apiId; 60 | private String parentId; 61 | private String resourceId; 62 | 63 | // README: see below for the full test 64 | // Since: https://github.com/localstack/localstack/issues/1030 has been fixed, it should work, but there's another issue 65 | // (on the test design this time: No integration defined for method "Service: ApiGateway" => investigate later) 66 | // For now we're just testing creation requests, but not the actual routing one, because localstack doesn't allow it 67 | @Test 68 | @Timeout(value = 10, timeUnit = TimeUnit.SECONDS) 69 | public void testRequestThroughGateway(Vertx vertx, VertxTestContext ctx) throws Exception { 70 | final Context originalContext = vertx.getOrCreateContext(); 71 | createGatewayClient(originalContext); 72 | // create the REST API 73 | createRestApi() 74 | .flatMap(restAPI -> { 75 | assertContext(vertx, originalContext, ctx); 76 | apiId = restAPI.id(); 77 | return getResources(); 78 | }) 79 | .flatMap(resources -> { 80 | assertContext(vertx, originalContext, ctx); 81 | List items = resources.items(); 82 | ctx.verify(() -> { 83 | assertEquals(1, items.size()); 84 | }); 85 | parentId = items.get(0).id(); 86 | return createResource(); 87 | }) 88 | .flatMap(createdRes -> { 89 | assertContext(vertx, originalContext, ctx); 90 | resourceId = createdRes.id(); 91 | return declareGetMethod(); 92 | }) 93 | .flatMap(putMethodRes -> { 94 | assertContext(vertx, originalContext, ctx); 95 | return declare200ResponseToGet(); 96 | }) 97 | .subscribe(res -> { 98 | assertContext(vertx, originalContext, ctx); 99 | ctx.completeNow(); 100 | }, ctx::failNow); 101 | } 102 | 103 | private Single createRestApi() { 104 | return single(gatewayClient.createRestApi(VertxApiGatewaySpec::restApiDefinition)); 105 | } 106 | 107 | private Single getResources() { 108 | return single(gatewayClient.getResources(grr -> grr.restApiId(apiId))); 109 | } 110 | 111 | private Single createResource() { 112 | return single(gatewayClient.createResource(crr -> crr.parentId(parentId).restApiId(apiId).pathPart(PATH))); 113 | } 114 | 115 | private Single declareGetMethod() { 116 | return single( 117 | gatewayClient.putMethod(pmr -> 118 | pmr.resourceId(resourceId) 119 | .restApiId(apiId) 120 | .httpMethod(METHOD.toString()) 121 | .authorizationType("NONE") 122 | ) 123 | ); 124 | } 125 | 126 | private Single declare200ResponseToGet() { 127 | return single( 128 | gatewayClient.putMethodResponse(pmr -> 129 | pmr.resourceId(resourceId) 130 | .restApiId(apiId) 131 | .httpMethod(METHOD.toString()) 132 | .statusCode(String.valueOf(HttpStatusCode.OK)) 133 | ) 134 | ); 135 | } 136 | 137 | private Single attachRemoteEndpoint() { 138 | return single( 139 | gatewayClient.putIntegration( pir -> 140 | pir.restApiId(apiId) 141 | .resourceId(parentId) 142 | .httpMethod(METHOD.toString()) 143 | .integrationHttpMethod(METHOD.toString()) 144 | .type(IntegrationType.MOCK) 145 | ) 146 | ); 147 | } 148 | 149 | private Single mapRemoteResponse() { 150 | return single( 151 | gatewayClient.putIntegrationResponse( pir -> 152 | pir.restApiId(apiId) 153 | .resourceId(parentId) 154 | .httpMethod(METHOD.toString()) 155 | .statusCode(String.valueOf(HttpStatusCode.OK)) 156 | .responseTemplates(TEMPLATES) 157 | ) 158 | ); 159 | } 160 | 161 | private Single makeIntegrationTest() { 162 | Map headers = new HashMap<>(1); 163 | headers.put("Accept", "text/plain"); 164 | return single( 165 | gatewayClient.testInvokeMethod(timr -> 166 | timr.restApiId(apiId) 167 | .resourceId(parentId) 168 | .pathWithQueryString(PATH) 169 | .httpMethod(METHOD.toString()) 170 | .headers(headers) 171 | ) 172 | ); 173 | } 174 | 175 | 176 | private static CreateRestApiRequest.Builder restApiDefinition(CreateRestApiRequest.Builder rar) { 177 | return rar.name(API_NAME) 178 | .binaryMediaTypes("text/plain") 179 | .description("Fetches_weather"); 180 | } 181 | 182 | private void createGatewayClient(Context context) throws Exception { 183 | final URI gatewayURI = new URI(Localstack.INSTANCE.getEndpointAPIGateway()); 184 | final ApiGatewayAsyncClientBuilder builder = ApiGatewayAsyncClient.builder() 185 | .region(Region.US_EAST_1) 186 | .credentialsProvider(credentialsProvider) 187 | .endpointOverride(gatewayURI); 188 | gatewayClient = VertxSdkClient.withVertx(builder, context).build(); 189 | } 190 | 191 | /* 192 | .flatMap(res -> { 193 | assertEquals(originalContext, vertx.getOrCreateContext()); 194 | return attachRemoteEndpoint(); 195 | }) 196 | .flatMap(res -> { 197 | assertEquals(originalContext, vertx.getOrCreateContext()); 198 | return mapRemoteResponse(); 199 | }) 200 | .flatMap(res -> { 201 | assertEquals(originalContext, vertx.getOrCreateContext()); 202 | return makeIntegrationTest(); 203 | }) 204 | .doOnSuccess(integrationTest -> { 205 | assertEquals(originalContext, vertx.getOrCreateContext()); 206 | assertEquals(integrationTest.body(), MOCK_RESPONSE); 207 | ctx.completeNow(); 208 | }) 209 | */ 210 | 211 | } 212 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/integration/cloudwatch/VertxCloudWatchClientSpec.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.integration.cloudwatch; 2 | 3 | import cloud.localstack.Localstack; 4 | import cloud.localstack.ServiceName; 5 | import cloud.localstack.docker.LocalstackDockerExtension; 6 | import cloud.localstack.docker.annotation.LocalstackDockerProperties; 7 | import io.reactiverse.awssdk.VertxSdkClient; 8 | import io.reactiverse.awssdk.integration.LocalStackBaseSpec; 9 | import io.vertx.core.Context; 10 | import io.vertx.core.Vertx; 11 | import io.vertx.core.json.JsonArray; 12 | import io.vertx.core.json.JsonObject; 13 | import io.vertx.junit5.Timeout; 14 | import io.vertx.junit5.VertxExtension; 15 | import io.vertx.junit5.VertxTestContext; 16 | import org.junit.jupiter.api.Test; 17 | import org.junit.jupiter.api.condition.EnabledIfSystemProperty; 18 | import org.junit.jupiter.api.extension.ExtendWith; 19 | import software.amazon.awssdk.regions.Region; 20 | import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClient; 21 | import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClientBuilder; 22 | import software.amazon.awssdk.services.cloudwatch.model.PutDashboardRequest; 23 | 24 | import java.net.URI; 25 | import java.util.concurrent.TimeUnit; 26 | 27 | import static org.junit.jupiter.api.Assertions.assertTrue; 28 | 29 | @EnabledIfSystemProperty(named = "tests.integration", matches = "localstack") 30 | @LocalstackDockerProperties(services = { ServiceName.CLOUDWATCH }, imageTag = "1.4.0") 31 | @ExtendWith(VertxExtension.class) 32 | @ExtendWith(LocalstackDockerExtension.class) 33 | public class VertxCloudWatchClientSpec extends LocalStackBaseSpec { 34 | 35 | private final static String DASHBOARD_NAME = "my-vertx-dashboard"; 36 | /* 37 | From AWS doc example: 38 | { 39 | widgets:[ 40 | { 41 | "type":"text", 42 | "x":0, 43 | "y":7, 44 | "width":3, 45 | "height":3, 46 | "properties":{ 47 | "markdown":"Hello world" 48 | } 49 | } 50 | ] 51 | } 52 | */ 53 | private final static JsonObject MARKDOWN_WIDGET = new JsonObject() 54 | .put("type", "text") 55 | .put("x", 0) 56 | .put("y", 7) 57 | .put("width", 3) 58 | .put("height", 3) 59 | .put("properties", new JsonObject().put("markdown", "Hello Vert.x")); 60 | private final static JsonArray DASHBOARD_WIDGETS = new JsonArray().add(MARKDOWN_WIDGET); 61 | private final static JsonObject DASHBOARD_BODY = new JsonObject().put("widgets", DASHBOARD_WIDGETS); 62 | 63 | 64 | @Test 65 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 66 | public void testCreateDashboard(Vertx vertx, VertxTestContext ctx) throws Exception { 67 | final Context originalContext = vertx.getOrCreateContext(); 68 | final CloudWatchAsyncClient cloudwatch = cloudwatchClient(originalContext); 69 | // TODO: create an alarm and send metric data to trigger alarm ? 70 | single(cloudwatch.putDashboard(VertxCloudWatchClientSpec::createDashboard)) 71 | .doOnSuccess(createRes -> { 72 | assertContext(vertx, originalContext, ctx); 73 | ctx.verify(() -> { 74 | assertTrue(createRes.dashboardValidationMessages().isEmpty()); 75 | ctx.completeNow(); 76 | }); 77 | }) 78 | .doOnError(ctx::failNow) 79 | .subscribe(); 80 | } 81 | 82 | private static PutDashboardRequest.Builder createDashboard(PutDashboardRequest.Builder pdr) { 83 | return pdr.dashboardName(DASHBOARD_NAME) 84 | .dashboardBody(DASHBOARD_BODY.encode()); 85 | } 86 | 87 | private static CloudWatchAsyncClient cloudwatchClient(Context ctx) throws Exception { 88 | final URI cloudwatchURI = new URI(Localstack.INSTANCE.getEndpointCloudWatch()); 89 | final CloudWatchAsyncClientBuilder builder = 90 | CloudWatchAsyncClient.builder() 91 | .credentialsProvider(credentialsProvider) 92 | .region(Region.EU_WEST_1) 93 | .endpointOverride(cloudwatchURI); 94 | return VertxSdkClient.withVertx(builder, ctx).build(); 95 | } 96 | 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/integration/dynamodb/VertxDynamoClientSpec.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.integration.dynamodb; 2 | 3 | import cloud.localstack.Localstack; 4 | import cloud.localstack.ServiceName; 5 | import cloud.localstack.docker.LocalstackDockerExtension; 6 | import cloud.localstack.docker.annotation.LocalstackDockerProperties; 7 | import io.reactiverse.awssdk.VertxSdkClient; 8 | import io.reactiverse.awssdk.integration.LocalStackBaseSpec; 9 | import io.vertx.core.Context; 10 | import io.vertx.core.Vertx; 11 | import io.vertx.junit5.Timeout; 12 | import io.vertx.junit5.VertxExtension; 13 | import io.vertx.junit5.VertxTestContext; 14 | import org.junit.jupiter.api.MethodOrderer; 15 | import org.junit.jupiter.api.Order; 16 | import org.junit.jupiter.api.Test; 17 | import org.junit.jupiter.api.TestMethodOrder; 18 | import org.junit.jupiter.api.condition.EnabledIfSystemProperty; 19 | import org.junit.jupiter.api.extension.ExtendWith; 20 | import software.amazon.awssdk.regions.Region; 21 | import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; 22 | import software.amazon.awssdk.services.dynamodb.model.AttributeValue; 23 | import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; 24 | import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; 25 | import software.amazon.awssdk.services.dynamodb.model.KeyType; 26 | import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; 27 | import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; 28 | 29 | import java.net.URI; 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | import java.util.concurrent.TimeUnit; 33 | 34 | import static org.junit.jupiter.api.Assertions.assertEquals; 35 | import static org.junit.jupiter.api.Assertions.assertNotNull; 36 | import static org.junit.jupiter.api.Assertions.assertTrue; 37 | 38 | @ExtendWith(VertxExtension.class) 39 | @ExtendWith(LocalstackDockerExtension.class) 40 | @EnabledIfSystemProperty(named = "tests.integration", matches = "localstack") 41 | @LocalstackDockerProperties(services = { ServiceName.DYNAMO }, imageTag = "1.4.0") 42 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 43 | public class VertxDynamoClientSpec extends LocalStackBaseSpec { 44 | 45 | private final static String TABLE_NAME = "BOOKS"; 46 | private final static String ISBN_FIELD = "isbn"; 47 | private final static String ISBN_VALUE = "9781617295621"; 48 | private final static Map ITEM = new HashMap<>(); 49 | static { 50 | ITEM.put(ISBN_FIELD, AttributeValue.builder().s(ISBN_VALUE).build()); 51 | } 52 | 53 | @Test 54 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 55 | @Order(1) 56 | public void createTable(Vertx vertx, VertxTestContext ctx) throws Exception { 57 | final Context originalContext = vertx.getOrCreateContext(); 58 | final DynamoDbAsyncClient dynamo = dynamo(originalContext); 59 | single(dynamo.createTable(VertxDynamoClientSpec::createTable)) 60 | .subscribe(createRes -> { 61 | assertContext(vertx, originalContext, ctx); 62 | ctx.completeNow(); 63 | }, ctx::failNow); 64 | } 65 | 66 | @Test 67 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 68 | @Order(2) 69 | public void listTables(Vertx vertx, VertxTestContext ctx) throws Exception { 70 | final Context originalContext = vertx.getOrCreateContext(); 71 | final DynamoDbAsyncClient dynamo = dynamo(originalContext); 72 | single(dynamo.listTables()) 73 | .subscribe(listResp -> { 74 | assertContext(vertx, originalContext, ctx); 75 | ctx.verify(() -> { 76 | assertTrue(listResp.tableNames().contains(TABLE_NAME)); 77 | ctx.completeNow(); 78 | }); 79 | }, ctx::failNow); 80 | } 81 | 82 | @Test 83 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 84 | @Order(3) 85 | public void addItemToTable(Vertx vertx, VertxTestContext ctx) throws Exception { 86 | final Context originalContext = vertx.getOrCreateContext(); 87 | final DynamoDbAsyncClient dynamo = dynamo(originalContext); 88 | single(dynamo.putItem(VertxDynamoClientSpec::putItemReq)) 89 | .subscribe(putRes -> { 90 | assertContext(vertx, originalContext, ctx); 91 | ctx.completeNow(); 92 | }, ctx::failNow); 93 | } 94 | 95 | @Test 96 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 97 | @Order(4) 98 | public void getItemFromTable(Vertx vertx, VertxTestContext ctx) throws Exception { 99 | final Context originalContext = vertx.getOrCreateContext(); 100 | final DynamoDbAsyncClient dynamo = dynamo(originalContext); 101 | single(dynamo.getItem(VertxDynamoClientSpec::getItem)) 102 | .subscribe(getRes -> { 103 | assertContext(vertx, originalContext, ctx); 104 | final AttributeValue isbn = getRes.item().get(ISBN_FIELD); 105 | ctx.verify(() -> { 106 | assertNotNull(isbn); 107 | assertEquals(isbn.s(), ISBN_VALUE); 108 | ctx.completeNow(); 109 | }); 110 | }, ctx::failNow); 111 | } 112 | 113 | private static CreateTableRequest.Builder createTable(CreateTableRequest.Builder builder) { 114 | return builder 115 | .tableName(TABLE_NAME) 116 | .provisionedThroughput(ps -> 117 | ps.writeCapacityUnits(40000L) 118 | .readCapacityUnits(40000L) 119 | ).attributeDefinitions(ad -> 120 | ad.attributeName(ISBN_FIELD) 121 | .attributeType(ScalarAttributeType.S) 122 | ).keySchema(ks -> 123 | ks.keyType(KeyType.HASH) 124 | .attributeName(ISBN_FIELD) 125 | ); 126 | } 127 | 128 | private static PutItemRequest.Builder putItemReq(PutItemRequest.Builder pib) { 129 | return pib.tableName(TABLE_NAME) 130 | .item(ITEM); 131 | } 132 | 133 | private static GetItemRequest.Builder getItem(GetItemRequest.Builder gib) { 134 | return gib.tableName(TABLE_NAME) 135 | .attributesToGet(ISBN_FIELD) 136 | .key(ITEM); 137 | } 138 | 139 | private DynamoDbAsyncClient dynamo(Context context) throws Exception { 140 | final URI dynamoEndpoint = new URI(Localstack.INSTANCE.getEndpointDynamoDB()); 141 | return VertxSdkClient.withVertx( 142 | DynamoDbAsyncClient.builder() 143 | .region(Region.EU_WEST_1) 144 | .credentialsProvider(credentialsProvider) 145 | .endpointOverride(dynamoEndpoint) 146 | , context) 147 | .build(); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/integration/firehose/VertxFirehoseClientSpec.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.integration.firehose; 2 | 3 | import cloud.localstack.Localstack; 4 | import cloud.localstack.ServiceName; 5 | import cloud.localstack.docker.LocalstackDockerExtension; 6 | import cloud.localstack.docker.annotation.LocalstackDockerProperties; 7 | import io.reactiverse.awssdk.VertxSdkClient; 8 | import io.reactiverse.awssdk.integration.LocalStackBaseSpec; 9 | import io.reactivex.Single; 10 | import io.vertx.core.Context; 11 | import io.vertx.core.Vertx; 12 | import io.vertx.core.json.JsonObject; 13 | import io.vertx.junit5.Timeout; 14 | import io.vertx.junit5.VertxExtension; 15 | import io.vertx.junit5.VertxTestContext; 16 | import io.vertx.reactivex.RxHelper; 17 | import org.junit.jupiter.api.BeforeAll; 18 | import org.junit.jupiter.api.Test; 19 | import org.junit.jupiter.api.condition.EnabledIfSystemProperty; 20 | import org.junit.jupiter.api.extension.ExtendWith; 21 | import org.slf4j.LoggerFactory; 22 | import software.amazon.awssdk.core.ResponseBytes; 23 | import software.amazon.awssdk.core.SdkBytes; 24 | import software.amazon.awssdk.core.SdkSystemSetting; 25 | import software.amazon.awssdk.core.async.AsyncResponseTransformer; 26 | import software.amazon.awssdk.regions.Region; 27 | import software.amazon.awssdk.services.firehose.FirehoseAsyncClient; 28 | import software.amazon.awssdk.services.firehose.FirehoseAsyncClientBuilder; 29 | import software.amazon.awssdk.services.firehose.FirehoseClient; 30 | import software.amazon.awssdk.services.firehose.model.CreateDeliveryStreamResponse; 31 | import software.amazon.awssdk.services.firehose.model.DeliveryStreamDescription; 32 | import software.amazon.awssdk.services.firehose.model.DeliveryStreamStatus; 33 | import software.amazon.awssdk.services.firehose.model.DeliveryStreamType; 34 | import software.amazon.awssdk.services.firehose.model.PutRecordRequest; 35 | import software.amazon.awssdk.services.firehose.model.PutRecordResponse; 36 | import software.amazon.awssdk.services.firehose.model.Record; 37 | import software.amazon.awssdk.services.s3.S3AsyncClient; 38 | import software.amazon.awssdk.services.s3.S3Client; 39 | import software.amazon.awssdk.services.s3.model.GetObjectResponse; 40 | import software.amazon.awssdk.services.s3.model.ListObjectsResponse; 41 | import software.amazon.awssdk.services.s3.model.S3Object; 42 | 43 | import java.net.URI; 44 | import java.time.Instant; 45 | import java.time.temporal.ChronoUnit; 46 | import java.util.List; 47 | import java.util.concurrent.TimeUnit; 48 | 49 | import static org.junit.jupiter.api.Assertions.assertEquals; 50 | import static org.junit.jupiter.api.Assertions.assertNotNull; 51 | import static org.junit.jupiter.api.Assertions.assertTrue; 52 | 53 | @ExtendWith(VertxExtension.class) 54 | @ExtendWith(LocalstackDockerExtension.class) 55 | @EnabledIfSystemProperty(named = "tests.integration", matches = "localstack") 56 | @LocalstackDockerProperties(services = { ServiceName.FIREHOSE, ServiceName.S3 }, imageTag = "1.4.0") 57 | public class VertxFirehoseClientSpec extends LocalStackBaseSpec { 58 | 59 | private final static String STREAM = "My-Vertx-Firehose-Stream"; 60 | private final static DeliveryStreamType STREAM_TYPE = DeliveryStreamType.DIRECT_PUT; 61 | private final static String BUCKET = "firehose-bucket"; 62 | private final static JsonObject FAKE_DATA = new JsonObject().put("producer", "vert.x"); 63 | 64 | private FirehoseAsyncClient firehoseClient; 65 | private S3AsyncClient s3Client; 66 | 67 | /** 68 | * Just create the Stream in a synchronous fashion, not using Vert.x 69 | * And wait for the stream to be created & ready 70 | */ 71 | @BeforeAll 72 | public static void createStream() throws Exception { 73 | System.setProperty(SdkSystemSetting.CBOR_ENABLED.property(), "false"); 74 | S3Client s3sync = S3Client.builder() 75 | .credentialsProvider(credentialsProvider) 76 | .region(Region.EU_WEST_1) 77 | .endpointOverride(s3URI()) 78 | .build(); 79 | // Create Sink bucket 80 | s3sync.createBucket(crb -> crb.bucket(BUCKET).acl("public-read-write")); 81 | List beforeObjects = s3sync.listObjects(lor -> lor.bucket(BUCKET)).contents(); 82 | assertTrue(beforeObjects.isEmpty()); 83 | // Create delivery stream, pointing to Sink S3 bucket 84 | FirehoseClient firehoseSync = FirehoseClient.builder() 85 | .region(Region.EU_WEST_1) 86 | .credentialsProvider(credentialsProvider) 87 | .endpointOverride(getFirehoseURI()) 88 | .build(); 89 | CreateDeliveryStreamResponse resp = firehoseSync.createDeliveryStream(cs -> 90 | cs.deliveryStreamName(STREAM) 91 | .deliveryStreamType(STREAM_TYPE) 92 | .extendedS3DestinationConfiguration(dest -> 93 | dest.bucketARN("arn:aws:s3:::" + BUCKET) 94 | ) 95 | ); 96 | assertNotNull(resp); 97 | // README: DeliveryStream creation can take some time. We have to wait or the Stream to be "ACTIVE" before making actual tests 98 | boolean streamReady = false; 99 | while(!streamReady) { 100 | // AWS recommendation: polling-frequency (even for DescribeStream) <= 1000ms 101 | Thread.sleep(1000L); // NOSONAR 102 | final DeliveryStreamDescription desc = firehoseSync.describeDeliveryStream(ds -> ds.deliveryStreamName(STREAM)).deliveryStreamDescription(); 103 | streamReady = desc.deliveryStreamStatus().equals(DeliveryStreamStatus.ACTIVE); 104 | assertEquals(STREAM_TYPE, desc.deliveryStreamType()); 105 | assertEquals(STREAM, desc.deliveryStreamName()); 106 | } 107 | } 108 | 109 | @Test 110 | @Timeout(value = 25, timeUnit = TimeUnit.SECONDS) 111 | public void testPublish(Vertx vertx, VertxTestContext ctx) throws Exception { 112 | final Context originalContext = vertx.getOrCreateContext(); 113 | createS3Client(originalContext); 114 | createFirehoseClient(originalContext); 115 | publishTestData() 116 | .delay(5, TimeUnit.SECONDS, RxHelper.scheduler(vertx)) // Let localstack deal with Firehose to S3 117 | .flatMap(pubRes -> { 118 | assertContext(vertx, originalContext, ctx); 119 | return lists3Files(); 120 | }) 121 | .subscribe(listRes -> { 122 | assertContext(vertx, originalContext, ctx); 123 | final List files = listRes.contents(); 124 | ctx.verify(() -> { 125 | assertEquals(1, files.size()); 126 | ctx.completeNow(); 127 | }); 128 | }, ctx::failNow); 129 | } 130 | 131 | private Single lists3Files() { 132 | return single( 133 | s3Client.listObjects(lor -> 134 | lor.bucket(BUCKET) 135 | ) 136 | ); 137 | } 138 | 139 | private Single> getFile(String bucket, String key) { 140 | LoggerFactory.getLogger(VertxFirehoseClientSpec.class).error("GETTING {} from S3", key); 141 | return single( 142 | s3Client.getObject(gor -> 143 | gor.bucket(bucket) 144 | .key(key) 145 | .ifModifiedSince(Instant.now().minus(2, ChronoUnit.HOURS)) 146 | , AsyncResponseTransformer.toBytes()) 147 | ); 148 | } 149 | 150 | private Single publishTestData() { 151 | return single( 152 | firehoseClient.putRecord(VertxFirehoseClientSpec::testRecord) 153 | ); 154 | } 155 | 156 | private static PutRecordRequest.Builder testRecord(PutRecordRequest.Builder prb) { 157 | final Record rec = Record.builder() 158 | .data(SdkBytes.fromUtf8String(FAKE_DATA.encode())) 159 | .build(); 160 | return prb.deliveryStreamName(STREAM) 161 | .record(rec); 162 | } 163 | 164 | private void createS3Client(Context context) throws Exception { 165 | s3Client = s3(context); 166 | } 167 | 168 | private void createFirehoseClient(Context context) throws Exception { 169 | final FirehoseAsyncClientBuilder builder = FirehoseAsyncClient.builder() 170 | .region(Region.EU_WEST_1) 171 | .credentialsProvider(credentialsProvider) 172 | .endpointOverride(getFirehoseURI()); 173 | firehoseClient = VertxSdkClient.withVertx(builder, context).build(); 174 | } 175 | 176 | private static URI getFirehoseURI() throws Exception { 177 | return new URI(Localstack.INSTANCE.getEndpointFirehose()); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/integration/kinesis/VertxKinesisClientSpec.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.integration.kinesis; 2 | 3 | import cloud.localstack.Localstack; 4 | import cloud.localstack.ServiceName; 5 | import cloud.localstack.docker.LocalstackDockerExtension; 6 | import cloud.localstack.docker.annotation.LocalstackDockerProperties; 7 | import io.reactiverse.awssdk.VertxSdkClient; 8 | import io.reactiverse.awssdk.integration.LocalStackBaseSpec; 9 | import io.vertx.core.Context; 10 | import io.vertx.core.Vertx; 11 | import io.vertx.junit5.Timeout; 12 | import io.vertx.junit5.VertxExtension; 13 | import io.vertx.junit5.VertxTestContext; 14 | import org.junit.jupiter.api.BeforeAll; 15 | import org.junit.jupiter.api.Test; 16 | import org.junit.jupiter.api.condition.EnabledIfSystemProperty; 17 | import org.junit.jupiter.api.extension.ExtendWith; 18 | import software.amazon.awssdk.core.SdkBytes; 19 | import software.amazon.awssdk.core.SdkSystemSetting; 20 | import software.amazon.awssdk.regions.Region; 21 | import software.amazon.awssdk.services.kinesis.KinesisAsyncClient; 22 | import software.amazon.awssdk.services.kinesis.KinesisAsyncClientBuilder; 23 | import software.amazon.awssdk.services.kinesis.KinesisClient; 24 | import software.amazon.awssdk.services.kinesis.model.CreateStreamResponse; 25 | import software.amazon.awssdk.services.kinesis.model.DescribeStreamRequest; 26 | import software.amazon.awssdk.services.kinesis.model.GetShardIteratorRequest; 27 | import software.amazon.awssdk.services.kinesis.model.PutRecordResponse; 28 | import software.amazon.awssdk.services.kinesis.model.Record; 29 | import software.amazon.awssdk.services.kinesis.model.ShardIteratorType; 30 | import software.amazon.awssdk.services.kinesis.model.StreamDescription; 31 | import software.amazon.awssdk.services.kinesis.model.StreamStatus; 32 | 33 | import java.net.URI; 34 | import java.util.List; 35 | import java.util.concurrent.CompletableFuture; 36 | import java.util.concurrent.TimeUnit; 37 | import java.util.function.Consumer; 38 | 39 | import static org.junit.jupiter.api.Assertions.assertEquals; 40 | import static org.junit.jupiter.api.Assertions.assertNotNull; 41 | 42 | @ExtendWith(VertxExtension.class) 43 | @ExtendWith(LocalstackDockerExtension.class) 44 | @EnabledIfSystemProperty(named = "tests.integration", matches = "localstack") 45 | @LocalstackDockerProperties(services = { ServiceName.KINESIS }, imageTag = "1.4.0") 46 | public class VertxKinesisClientSpec extends LocalStackBaseSpec { 47 | 48 | private final static String STREAM = "my-awesome-stream"; 49 | private final static SdkBytes DATA = SdkBytes.fromByteArray("Hello".getBytes()); 50 | 51 | private String currentShardIterator; 52 | private long pollTimer = -1; 53 | private long streamTimer = -1; 54 | 55 | /** 56 | * Just create the Stream in a synchronous fashion, not using Vert.x 57 | * And wait for the stream to be created & ready (with the right nb. of shards) 58 | */ 59 | @BeforeAll 60 | public static void createStream() throws Exception { 61 | System.setProperty(SdkSystemSetting.CBOR_ENABLED.property(), "false"); 62 | KinesisClient client = KinesisClient.builder() 63 | .region(Region.EU_WEST_1) 64 | .credentialsProvider(credentialsProvider) 65 | .endpointOverride(new URI(Localstack.INSTANCE.getEndpointKinesis())) 66 | .build(); 67 | CreateStreamResponse resp = client.createStream(cs -> cs.streamName(STREAM).shardCount(1)); 68 | assertNotNull(resp); 69 | // README: Stream creation can take some time. We have to wait or the Stream to be "ACTIVE" before making actual tests 70 | boolean streamReady = false; 71 | while(!streamReady) { 72 | // AWS recommendation: polling-frequency (even for DescribeStream) <= 1000ms 73 | Thread.sleep(1000L); // NOSONAR 74 | final StreamDescription desc = client.describeStream(ds -> ds.streamName(STREAM)).streamDescription(); 75 | streamReady = desc.streamStatus().equals(StreamStatus.ACTIVE); 76 | if (streamReady) { 77 | // Check that we have the correct number of shards before starting the actual testing 78 | assertEquals(1, desc.shards().size()); 79 | } 80 | } 81 | } 82 | 83 | @Test 84 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 85 | public void testPubSub(Vertx vertx, VertxTestContext ctx) throws Exception { 86 | final Context originalContext = vertx.getOrCreateContext(); 87 | final KinesisAsyncClient kinesis = kinesis(originalContext); 88 | single(kinesis.describeStream(this::streamDesc)) 89 | .flatMap(descRes -> { 90 | assertContext(vertx, originalContext, ctx); 91 | String shardId = descRes.streamDescription().shards().get(0).shardId(); 92 | return single(kinesis.getShardIterator(this.shardIterator(shardId))); 93 | }).subscribe(getShardRes -> { 94 | assertContext(vertx, originalContext, ctx); 95 | startPolling(vertx, ctx, kinesis, originalContext, getShardRes.shardIterator()); 96 | publishTestRecord(kinesis); 97 | }, ctx::failNow); 98 | } 99 | 100 | private CompletableFuture publishTestRecord(KinesisAsyncClient kinesis) { 101 | return kinesis.putRecord(pr -> { 102 | pr.streamName(STREAM) 103 | .partitionKey("Hello") // we don't care there's only 1 shard 104 | .data(DATA); 105 | }); 106 | } 107 | 108 | 109 | /** 110 | * Polls the shard 111 | * Will complete the test once it has received the record we need to send 112 | */ 113 | private void startPolling(Vertx vertx, VertxTestContext ctx, KinesisAsyncClient kinesis, Context originalContext, String shardIteratorId) { 114 | currentShardIterator = shardIteratorId; 115 | vertx.setPeriodic(1000L, t -> { 116 | pollTimer = t; 117 | kinesis.getRecords(rc -> 118 | rc.shardIterator(currentShardIterator).limit(1) 119 | ).handle((getRecRes, getRecError) -> { 120 | if (getRecError != null) { 121 | ctx.failNow(getRecError); 122 | return null; 123 | } 124 | final List recs = getRecRes.records(); 125 | if (recs.size() > 0) { 126 | ctx.verify(() -> { 127 | assertEquals(1, recs.size()); 128 | assertEquals(DATA, recs.get(0).data()); 129 | }); 130 | if (pollTimer > -1) { 131 | vertx.cancelTimer(pollTimer); 132 | } 133 | ctx.completeNow(); 134 | } else { 135 | currentShardIterator = getRecRes.nextShardIterator(); 136 | } 137 | return null; 138 | }); 139 | }); 140 | } 141 | 142 | private void streamDesc(DescribeStreamRequest.Builder dsr) { 143 | dsr.streamName(STREAM); 144 | } 145 | 146 | private Consumer shardIterator(String shardId) { 147 | return gsi -> gsi.streamName(STREAM) 148 | .shardIteratorType(ShardIteratorType.LATEST) 149 | .shardId(shardId); 150 | } 151 | 152 | private KinesisAsyncClient kinesis(Context context) throws Exception { 153 | final URI kinesisURI = new URI(Localstack.INSTANCE.getEndpointKinesis()); 154 | final KinesisAsyncClientBuilder builder = KinesisAsyncClient.builder() 155 | .region(Region.EU_WEST_1) 156 | .endpointOverride(kinesisURI) 157 | .credentialsProvider(credentialsProvider); 158 | return VertxSdkClient.withVertx(builder, context).build(); 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/integration/lambda/VertxLambdaClientSpec.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.integration.lambda; 2 | 3 | import cloud.localstack.Localstack; 4 | import cloud.localstack.ServiceName; 5 | import cloud.localstack.docker.LocalstackDockerExtension; 6 | import cloud.localstack.docker.annotation.LocalstackDockerProperties; 7 | import io.reactiverse.awssdk.VertxSdkClient; 8 | import io.reactiverse.awssdk.integration.LocalStackBaseSpec; 9 | import io.reactiverse.awssdk.utils.ZipUtils; 10 | import io.reactivex.Single; 11 | import io.vertx.core.Context; 12 | import io.vertx.core.Vertx; 13 | import io.vertx.core.json.JsonObject; 14 | import io.vertx.junit5.Timeout; 15 | import io.vertx.junit5.VertxExtension; 16 | import io.vertx.junit5.VertxTestContext; 17 | import org.junit.jupiter.api.Disabled; 18 | import org.junit.jupiter.api.MethodOrderer; 19 | import org.junit.jupiter.api.Order; 20 | import org.junit.jupiter.api.Test; 21 | import org.junit.jupiter.api.TestMethodOrder; 22 | import org.junit.jupiter.api.condition.EnabledIfSystemProperty; 23 | import org.junit.jupiter.api.extension.ExtendWith; 24 | import software.amazon.awssdk.core.SdkBytes; 25 | import software.amazon.awssdk.regions.Region; 26 | import software.amazon.awssdk.services.lambda.LambdaAsyncClient; 27 | import software.amazon.awssdk.services.lambda.LambdaAsyncClientBuilder; 28 | import software.amazon.awssdk.services.lambda.model.CreateFunctionRequest; 29 | import software.amazon.awssdk.services.lambda.model.CreateFunctionResponse; 30 | import software.amazon.awssdk.services.lambda.model.InvokeRequest; 31 | import software.amazon.awssdk.services.lambda.model.InvokeResponse; 32 | import software.amazon.awssdk.services.lambda.model.Runtime; 33 | 34 | import java.net.URI; 35 | import java.util.concurrent.TimeUnit; 36 | 37 | import static org.junit.jupiter.api.Assertions.assertEquals; 38 | import static org.junit.jupiter.api.Assertions.assertNotNull; 39 | import static org.junit.jupiter.api.Assertions.assertNull; 40 | 41 | @EnabledIfSystemProperty(named = "tests.integration", matches = "localstack") 42 | @LocalstackDockerProperties(services = { ServiceName.LAMBDA }, imageTag = "1.4.0") 43 | @ExtendWith(VertxExtension.class) 44 | @ExtendWith(LocalstackDockerExtension.class) 45 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 46 | @Disabled // localstack broke lambda support in its latest version... 47 | public class VertxLambdaClientSpec extends LocalStackBaseSpec { 48 | 49 | private static final String LAMBDA_NAME = "My-Vertx-Lambda"; 50 | private static final String EXEC_ARG = "name"; 51 | private static final String ARG_VALUE = "Vert.x"; 52 | private static final JsonObject EXPECTED_PAYLOAD = new JsonObject() 53 | .put("message", "Hello " + ARG_VALUE + "!"); 54 | 55 | private LambdaAsyncClient lambdaClient; 56 | 57 | @Test 58 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 59 | @Order(1) 60 | public void createLambda(Vertx vertx, VertxTestContext ctx) throws Exception { 61 | final Context originalContext = vertx.getOrCreateContext(); 62 | lambdaClient = createLambdaClient(originalContext); 63 | createFunction() 64 | .subscribe(createRes -> { 65 | assertContext(vertx, originalContext, ctx); 66 | ctx.verify(() -> { 67 | assertEquals(LAMBDA_NAME, createRes.functionName()); 68 | ctx.completeNow(); 69 | }); 70 | }, ctx::failNow); 71 | } 72 | 73 | @Test 74 | @Timeout(value = 15, timeUnit = TimeUnit.SECONDS) 75 | @Order(2) 76 | public void invokeLambda(Vertx vertx, VertxTestContext ctx) throws Exception { 77 | final Context originalContext = vertx.getOrCreateContext(); 78 | lambdaClient = createLambdaClient(originalContext); 79 | invokeFunction() 80 | .subscribe(invokeRes -> { 81 | assertContext(vertx, originalContext, ctx); 82 | ctx.verify(() -> { 83 | assertNull(invokeRes.functionError()); 84 | final SdkBytes payload = invokeRes.payload(); 85 | assertNotNull(payload); 86 | final JsonObject received = new JsonObject(payload.asUtf8String()); 87 | assertEquals(EXPECTED_PAYLOAD, received); 88 | ctx.completeNow(); 89 | }); 90 | }, ctx::failNow); 91 | } 92 | 93 | private Single createFunction() { 94 | return single( 95 | lambdaClient.createFunction(VertxLambdaClientSpec::lambdaDefinition) 96 | ); 97 | } 98 | 99 | private Single invokeFunction() { 100 | return single( 101 | lambdaClient.invoke(VertxLambdaClientSpec::lambdaInvokation) 102 | ); 103 | } 104 | 105 | private static CreateFunctionRequest.Builder lambdaDefinition(CreateFunctionRequest.Builder fnb) { 106 | final SdkBytes functionFile = SdkBytes.fromByteArray(ZipUtils.zip("lambda", "greeter.py")); 107 | return fnb.functionName(LAMBDA_NAME) 108 | .handler("greeter") 109 | .runtime(Runtime.PYTHON3_6) 110 | .code(vb -> 111 | vb.zipFile(functionFile) 112 | ); 113 | } 114 | 115 | private static InvokeRequest.Builder lambdaInvokation(InvokeRequest.Builder irb) { 116 | final JsonObject payload = new JsonObject() 117 | .put(EXEC_ARG, ARG_VALUE); 118 | return irb.functionName(LAMBDA_NAME) 119 | .payload(SdkBytes.fromUtf8String(payload.encode())); 120 | } 121 | 122 | private static LambdaAsyncClient createLambdaClient(Context ctx) throws Exception { 123 | final URI lambdaURI = new URI(Localstack.INSTANCE.getEndpointLambda()); 124 | final LambdaAsyncClientBuilder builder = 125 | LambdaAsyncClient.builder() 126 | .credentialsProvider(credentialsProvider) 127 | .region(Region.EU_WEST_1) 128 | .endpointOverride(lambdaURI); 129 | return VertxSdkClient.withVertx(builder, ctx).build(); 130 | } 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/test/java/io/reactiverse/awssdk/utils/ZipUtils.java: -------------------------------------------------------------------------------- 1 | package io.reactiverse.awssdk.utils; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.zip.ZipEntry; 7 | import java.util.zip.ZipOutputStream; 8 | 9 | public interface ZipUtils { 10 | 11 | static byte[] zip(String prefix, String fileName) { 12 | final InputStream is = ClassLoader.getSystemResourceAsStream(prefix + "/" + fileName); 13 | final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 14 | final ZipOutputStream zos = new ZipOutputStream(bos); 15 | try { 16 | zos.putNextEntry(new ZipEntry(fileName)); 17 | int length; 18 | byte[] buffer = new byte[2048]; 19 | while ((length = is.read(buffer)) >= 0) { 20 | zos.write(buffer, 0, length); 21 | } 22 | zos.closeEntry(); 23 | zos.close(); 24 | } catch (IOException e) { 25 | throw new RuntimeException(e); 26 | } 27 | return bos.toByteArray(); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/resources/lambda/greeter.py: -------------------------------------------------------------------------------- 1 | def greeter(event, context): 2 | message = 'Hello {}!'.format(event['name']) 3 | return { 4 | 'message' : message 5 | } 6 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/test/resources/s3/cairn_little.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reactiverse/aws-sdk/a0204430baccff6c827d8ebfd9b8391d26a8fa09/src/test/resources/s3/cairn_little.jpg -------------------------------------------------------------------------------- /utils/Dockerfile.test: -------------------------------------------------------------------------------- 1 | # Dockerfile for running tests in a container (like Travis does) 2 | # from the root of the project: `mv utils/Dockerfile.test Dockerfile` then `docker build .` then `docker run ` 3 | FROM openjdk:9-jdk 4 | 5 | WORKDIR /tmp 6 | 7 | ADD src ./src 8 | ADD gradle ./gradle 9 | ADD build.gradle . 10 | ADD settings.gradle . 11 | ADD gradlew . 12 | 13 | ENTRYPOINT ./gradlew test 14 | --------------------------------------------------------------------------------