├── .github └── workflows │ └── build.yml ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── LICENCE ├── README.md ├── examples └── localstack-spring-boot-sample │ ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── sivalabs │ │ │ └── demo │ │ │ ├── LocalStackStarterDemoApplication.java │ │ │ └── services │ │ │ ├── DynamoDBService.java │ │ │ ├── S3Service.java │ │ │ ├── SNSService.java │ │ │ └── SQSService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── sivalabs │ └── demo │ ├── SpringbootLocalStackDemoApplicationTest.java │ └── services │ ├── CloudWatchTest.java │ ├── DynamoDBServiceTest.java │ ├── DynamoDBStreamsTest.java │ ├── IAMTest.java │ ├── KinesisTest.java │ ├── LambdaTest.java │ ├── S3ServiceTest.java │ ├── SNSServiceTest.java │ ├── SQSServiceTest.java │ └── SecretsManagerTest.java ├── maven-settings.xml ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main └── java │ └── io │ └── github │ └── sivalabs │ └── localstack │ ├── EnableLocalStack.java │ ├── LocalStackProperties.java │ └── autoconfigure │ ├── LocalStackAutoConfiguration.java │ └── configurator │ ├── AWSLambdaConfiguration.java │ ├── AWSSecretsManagerConfiguration.java │ ├── AbstractAmazonClient.java │ ├── AmazonCloudWatchConfiguration.java │ ├── AmazonDynamoDBConfiguration.java │ ├── AmazonDynamoDBStreamsConfiguration.java │ ├── AmazonIAMConfiguration.java │ ├── AmazonKinesisConfiguration.java │ ├── AmazonS3Configuration.java │ ├── AmazonSNSConfiguration.java │ ├── AmazonSQSConfiguration.java │ ├── ConditionalOnLocalStackService.java │ └── LocalStackContainerConfiguration.java └── test └── java └── io └── github └── sivalabs └── localstack └── autoconfigure └── LocalStackAutoConfigurationTest.java /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | 8 | jobs: 9 | Build: 10 | name: Unit & Integration Tests 11 | runs-on: ubuntu-18.04 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up JDK 1.8 17 | uses: actions/setup-java@v1 18 | with: 19 | java-version: 1.8 20 | 21 | - uses: actions/cache@v1 22 | with: 23 | path: ~/.m2/repository 24 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 25 | restore-keys: | 26 | ${{ runner.os }}-maven- 27 | - name: Build with Maven 28 | run: ./mvnw -B clean install 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivalabs/localstack-spring-boot-starter/c5acfac0916d0ffc1e0f89b104169c67d1005855/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 K. Siva Prasad Reddy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # localstack-spring-boot-starter 2 | 3 | ![Build](https://github.com/sivalabs/localstack-spring-boot-starter/workflows/Build/badge.svg) 4 | [![Maven Central](https://img.shields.io/maven-central/v/io.github.sivalabs/localstack-spring-boot-starter)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22io.github.sivalabs%22) 5 | [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://raw.githubusercontent.com/sivalabs/localstack-spring-boot-starter/master/LICENSE) 6 | 7 | `Localstack-spring-boot-starter` is a [SpringBoot](https://spring.io/projects/spring-boot) starter for [LocalStack](https://github.com/localstack/localstack) auto-configuration. 8 | This starter will spin up the *Localstack* docker container using [Testcontainers](https://www.testcontainers.org/) 9 | and auto-configure beans such as `AmazonS3`, `AmazonSQSAsync`, etc. 10 | 11 | ## Motivation 12 | [LocalStack](https://github.com/localstack/localstack) provides an easy-to-use test/mocking framework for developing AWS based Cloud applications. 13 | We can use [Testcontainers](https://www.testcontainers.org/modules/localstack/) to spin up a *Localstack* docker container, 14 | but we need to configure Amazon service clients like `AmazonS3`, `AmazonSQSAsync` which is typical boilerplate that we copy-paste from project to project. 15 | Instead of copy-pasting the code snippets, creating a SpringBoot starter which autoconfigures the Amazon service clients is a better approach and less error prone. 16 | Hence, the birth of `localstack-spring-boot-starter` :-) 17 | 18 | ## Requirements 19 | * JDK 8+ 20 | * Tested with SpringBoot 2.3.3.RELEASE, should work fine with any SpringBoot 2.x versions 21 | 22 | ## How to use? 23 | 24 | ### Add dependencies 25 | 26 | **Maven** 27 | 28 | ```xml 29 | 30 | 31 | io.github.sivalabs 32 | localstack-spring-boot-starter 33 | 0.0.1 34 | 35 | 36 | org.testcontainers 37 | localstack 38 | 1.14.3 39 | 40 | 41 | com.amazonaws 42 | aws-java-sdk 43 | 1.11.852 44 | 45 | 46 | ``` 47 | 48 | **Gradle** 49 | 50 | ```groovy 51 | implementation 'io.github.sivalabs:localstack-spring-boot-starter:0.0.1' 52 | implementation 'org.testcontainers:localstack:1.14.3' 53 | implementation 'com.amazonaws:aws-java-sdk:1.11.852' 54 | ``` 55 | 56 | ### Enable LocalStack AutoConfiguration 57 | You can enable LocalStack AutoConfiguration by adding `@EnableLocalStack` annotation to either main entrypoint class or 58 | any `@Configuration` class. 59 | 60 | ```java 61 | package com.sivalabs.demo; 62 | 63 | import io.github.sivalabs.localstack.EnableLocalStack; 64 | import org.springframework.boot.SpringApplication; 65 | import org.springframework.boot.autoconfigure.SpringBootApplication; 66 | import com.amazonaws.services.s3.AmazonS3; 67 | import com.amazonaws.services.sqs.AmazonSQSAsync; 68 | import org.springframework.beans.factory.annotation.Autowired; 69 | 70 | @SpringBootApplication 71 | @EnableLocalStack 72 | public class LocalStackStarterDemoApplication { 73 | 74 | @Autowired 75 | private AmazonS3 amazonS3; 76 | 77 | @Autowired 78 | private AmazonSQSAsync amazonSQS; 79 | 80 | public static void main(String[] args) { 81 | SpringApplication.run(LocalStackStarterDemoApplication.class, args); 82 | } 83 | } 84 | ``` 85 | 86 | #### How to use only for Integration Tests? 87 | You may want to use `localstack-spring-boot-starter` only for testing. 88 | In that case, you can add `@EnableLocalStack` annotation combined with `@Profile("integration-test")` annotation 89 | so that the Localstack AutoConfiguration is only activated while running integration tests. 90 | 91 | ```java 92 | @Configuration 93 | @EnableLocalStack 94 | @Profile("integration-test") 95 | public class TestConfig { 96 | } 97 | ``` 98 | 99 | You can activate `integration-test` profile using `@ActiveProfiles` as follows: 100 | 101 | ```java 102 | @SpringBootTest 103 | @ActiveProfiles("integration-test") 104 | class SomeIntegrationTest { 105 | @Autowired 106 | private AmazonS3 amazonS3; 107 | 108 | @Autowired 109 | private AmazonSQSAsync amazonSQS; 110 | 111 | @Test 112 | void someTest() { 113 | 114 | } 115 | } 116 | ``` 117 | 118 | ### Configuration 119 | 120 | The following configuration properties are available to customize the default behaviour. 121 | 122 | | Property | Required | Default Value | 123 | |-------------------------------|----------|--------------------------------| 124 | | `localstack.enabled` | no | `true` | 125 | | `localstack.edgePort` | no | `4566` | 126 | | `localstack.defaultRegion` | no | `us-east-1` | 127 | | `localstack.hostname` | no | `localhost` | 128 | | `localstack.hostnameExternal` | no | `localhost` | 129 | | `localstack.dockerImage` | no | `localstack/localstack:0.11.2` | 130 | | `localstack.useSsl` | no | `false` | 131 | | `localstack.services` | no | `""` | 132 | 133 | You can customize which AWS services to enable/disable as follows: 134 | 135 | | Property | Value | Default Value | 136 | |--------------------------------------|---------------------------------------------------------------------------------------------|---------------| 137 | | `localstack.services` | `SQS, S3, SNS, DYNAMODB, DYNAMODBSTREAMS, KINESIS, IAM, LAMBDA, CLOUDWATCH, SECRETSMANAGER` | `""` | 138 | | `localstack.s3.enabled` | `false` | `true` | 139 | | `localstack.sqs.enabled` | `true` | `true` | 140 | | `localstack.sns.enabled` | `false` | `true` | 141 | | `localstack.dynamodb.enabled` | `true` | `true` | 142 | | `localstack.dynamodbstreams.enabled` | `false` | `true` | 143 | | `localstack.kinesis.enabled` | `true` | `true` | 144 | | `localstack.iam.enabled` | `false` | `true` | 145 | | `localstack.secretsmanager.enabled` | `true` | `true` | 146 | | `localstack.lambda.enabled` | `false` | `true` | 147 | | `localstack.cloudwatch.enabled` | `true` | `true` | 148 | 149 | ## Examples 150 | * [Minimal SpringBoot application](https://github.com/sivalabs/localstack-spring-boot-starter/tree/master/examples/localstack-spring-boot-sample) 151 | 152 | ## Want to Contribute? 153 | 154 | You can contribute to `localstack-spring-boot-starter` project in many ways: 155 | * Use the starter and report if there are any bugs by [opening an issue](https://github.com/sivalabs/localstack-spring-boot-starter/issues/new) 156 | * Add support for auto-configuration of [more services](https://github.com/localstack/localstack#overview) 157 | * Add more [example applications](https://github.com/sivalabs/localstack-spring-boot-starter/tree/v0.0.2/examples) 158 | 159 | ## Credits 160 | The implementation of `localstack-spring-boot-starter` is inspired by [testcontainers-spring-boot](https://github.com/testcontainers/testcontainers-spring-boot). 161 | The `testcontainers-spring-boot` also provides localstack support, but it only spins up the docker container, and 162 | you will have to configure the beans like `AmazonS3`, `AmazonSQSAsync` etc by yourself. 163 | 164 | ## License 165 | [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://raw.githubusercontent.com/sivalabs/localstack-spring-boot-starter/master/LICENSE) 166 | 167 | ## Developer Notes 168 | 169 | Procedure for deploying to Maven Central https://central.sonatype.org/pages/apache-maven.html 170 | 171 | Set version to SNAPSHOT (ex: 1.0.0-SNAPSHOT) 172 | 173 | Deploy SNAPSHOT version to https://oss.sonatype.org/content/repositories/snapshots/ 174 | 175 | ```shell script 176 | localstack-spring-boot-starter> ./mvnw clean deploy -Prelease 177 | ``` 178 | 179 | Deploy release version to Maven Central 180 | 181 | ```shell script 182 | localstack-spring-boot-starter> ./mvnw release:clean release:prepare -Prelease 183 | localstack-spring-boot-starter> ./mvnw release:perform -Prelease 184 | ``` 185 | 186 | Search for release artifacts on https://oss.sonatype.org/#nexus-search;quick~sivalabs 187 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sivalabs/localstack-spring-boot-starter/c5acfac0916d0ffc1e0f89b104169c67d1005855/examples/localstack-spring-boot-sample/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip 2 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | com.sivalabs 8 | localstack-spring-boot-sample 9 | jar 10 | 1.0-SNAPSHOT 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 2.7.10 16 | 17 | 18 | 19 | UTF-8 20 | 1.8 21 | 1.11.852 22 | 1.17.6 23 | 0.0.3-SNAPSHOT 24 | 4.2.0 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-maven-plugin 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.testcontainers 40 | testcontainers-bom 41 | ${testcontainers.version} 42 | pom 43 | import 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-web 52 | 53 | 54 | org.testcontainers 55 | localstack 56 | 57 | 58 | io.github.sivalabs 59 | localstack-spring-boot-starter 60 | ${localstack-starter.version} 61 | 62 | 63 | com.amazonaws 64 | aws-java-sdk 65 | ${aws-java-sdk.version} 66 | 67 | 68 | org.springframework.boot 69 | spring-boot-starter-test 70 | test 71 | 72 | 73 | org.awaitility 74 | awaitility 75 | ${awaitility.version} 76 | test 77 | 78 | 79 | org.projectlombok 80 | lombok 81 | true 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/main/java/com/sivalabs/demo/LocalStackStarterDemoApplication.java: -------------------------------------------------------------------------------- 1 | 2 | package com.sivalabs.demo; 3 | 4 | import io.github.sivalabs.localstack.EnableLocalStack; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | 8 | @SpringBootApplication 9 | @EnableLocalStack 10 | public class LocalStackStarterDemoApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(LocalStackStarterDemoApplication.class, args); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/main/java/com/sivalabs/demo/services/DynamoDBService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync; 4 | import com.amazonaws.services.dynamodbv2.model.ListTablesRequest; 5 | import com.amazonaws.services.dynamodbv2.model.ListTablesResult; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | 11 | @Service 12 | @RequiredArgsConstructor 13 | public class DynamoDBService { 14 | private final AmazonDynamoDBAsync amazonDynamoDBAsync; 15 | 16 | public List listTables() { 17 | ListTablesRequest request = new ListTablesRequest().withLimit(10); 18 | ListTablesResult tableList = amazonDynamoDBAsync.listTables(request); 19 | return tableList.getTableNames(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/main/java/com/sivalabs/demo/services/S3Service.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | 4 | import com.amazonaws.AmazonServiceException; 5 | import com.amazonaws.SdkClientException; 6 | import com.amazonaws.services.s3.AmazonS3; 7 | import com.amazonaws.services.s3.model.Bucket; 8 | import com.amazonaws.services.s3.model.ListVersionsRequest; 9 | import com.amazonaws.services.s3.model.ObjectListing; 10 | import com.amazonaws.services.s3.model.ObjectMetadata; 11 | import com.amazonaws.services.s3.model.PutObjectResult; 12 | import com.amazonaws.services.s3.model.S3Object; 13 | import com.amazonaws.services.s3.model.S3ObjectSummary; 14 | import com.amazonaws.services.s3.model.S3VersionSummary; 15 | import com.amazonaws.services.s3.model.VersionListing; 16 | import lombok.RequiredArgsConstructor; 17 | import org.springframework.stereotype.Service; 18 | 19 | import java.io.ByteArrayInputStream; 20 | import java.io.InputStream; 21 | import java.util.Iterator; 22 | import java.util.List; 23 | 24 | @Service 25 | @RequiredArgsConstructor 26 | public class S3Service { 27 | private final AmazonS3 amazonS3; 28 | 29 | public Bucket createBucket(String bucketName) { 30 | return amazonS3.createBucket(bucketName); 31 | } 32 | 33 | public List listBuckets() { 34 | return amazonS3.listBuckets(); 35 | } 36 | 37 | public S3Object getObject(String bucketName, String key) { 38 | return amazonS3.getObject(bucketName, key); 39 | } 40 | 41 | public PutObjectResult store(String bucketName, String key, String value) { 42 | return amazonS3.putObject(bucketName, key, value); 43 | } 44 | 45 | public PutObjectResult store(String bucketName, String key, InputStream inputStream) { 46 | return amazonS3.putObject(bucketName, key, inputStream, new ObjectMetadata()); 47 | } 48 | 49 | public PutObjectResult store(String bucketName, String key, byte[] bytes) { 50 | InputStream inputStream = new ByteArrayInputStream(bytes); 51 | return store(bucketName, key, inputStream); 52 | } 53 | 54 | public void deleteBucket(String bucketName) { 55 | amazonS3.deleteBucket(bucketName); 56 | } 57 | 58 | public void deleteBucketForce(String bucketName) { 59 | try { 60 | // Delete all objects from the bucket. This is sufficient 61 | // for unversioned buckets. For versioned buckets, when you attempt to delete objects, Amazon S3 inserts 62 | // delete markers for all objects, but doesn't delete the object versions. 63 | // To delete objects from versioned buckets, delete all of the object versions before deleting 64 | // the bucket (see below for an example). 65 | ObjectListing objectListing = amazonS3.listObjects(bucketName); 66 | while (true) { 67 | Iterator objIter = objectListing.getObjectSummaries().iterator(); 68 | while (objIter.hasNext()) { 69 | amazonS3.deleteObject(bucketName, objIter.next().getKey()); 70 | } 71 | 72 | // If the bucket contains many objects, the listObjects() call 73 | // might not return all of the objects in the first listing. Check to 74 | // see whether the listing was truncated. If so, retrieve the next page of objects 75 | // and delete them. 76 | if (objectListing.isTruncated()) { 77 | objectListing = amazonS3.listNextBatchOfObjects(objectListing); 78 | } else { 79 | break; 80 | } 81 | } 82 | 83 | // Delete all object versions (required for versioned buckets). 84 | VersionListing versionList = amazonS3.listVersions(new ListVersionsRequest().withBucketName(bucketName)); 85 | while (true) { 86 | Iterator versionIter = versionList.getVersionSummaries().iterator(); 87 | while (versionIter.hasNext()) { 88 | S3VersionSummary vs = versionIter.next(); 89 | amazonS3.deleteVersion(bucketName, vs.getKey(), vs.getVersionId()); 90 | } 91 | 92 | if (versionList.isTruncated()) { 93 | versionList = amazonS3.listNextBatchOfVersions(versionList); 94 | } else { 95 | break; 96 | } 97 | } 98 | 99 | // After all objects and object versions are deleted, delete the bucket. 100 | amazonS3.deleteBucket(bucketName); 101 | } 102 | catch(AmazonServiceException e) { 103 | // The call was transmitted successfully, but Amazon S3 couldn't process 104 | // it, so it returned an error response. 105 | e.printStackTrace(); 106 | } 107 | catch(SdkClientException e) { 108 | // Amazon S3 couldn't be contacted for a response, or the client couldn't 109 | // parse the response from Amazon S3. 110 | e.printStackTrace(); 111 | } 112 | } 113 | 114 | } 115 | 116 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/main/java/com/sivalabs/demo/services/SNSService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.sns.AmazonSNSAsync; 4 | import com.amazonaws.services.sns.model.CreateTopicRequest; 5 | import com.amazonaws.services.sns.model.CreateTopicResult; 6 | import com.amazonaws.services.sns.model.DeleteTopicResult; 7 | import com.amazonaws.services.sns.model.PublishRequest; 8 | import com.amazonaws.services.sns.model.PublishResult; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.stereotype.Service; 12 | 13 | @Service 14 | @RequiredArgsConstructor 15 | @Slf4j 16 | public class SNSService { 17 | 18 | private final AmazonSNSAsync amazonSNSAsync; 19 | 20 | public CreateTopicResult createTopic(String topicName) { 21 | CreateTopicRequest createTopicRequest = new CreateTopicRequest(topicName); 22 | CreateTopicResult createTopicResponse = amazonSNSAsync.createTopic(createTopicRequest); 23 | log.debug("Created SNS Topic : {}", createTopicResponse.getTopicArn()); 24 | return createTopicResponse; 25 | } 26 | 27 | public DeleteTopicResult deleteTopic(String topicName) { 28 | CreateTopicResult topic = createTopic(topicName); 29 | return amazonSNSAsync.deleteTopic(topic.getTopicArn()); 30 | } 31 | 32 | public PublishResult sendMessage(String topicName, String msg) { 33 | CreateTopicResult topic = createTopic(topicName); 34 | PublishRequest publishRequest = new PublishRequest(topic.getTopicArn(), msg); 35 | PublishResult publishResponse = amazonSNSAsync.publish(publishRequest); 36 | log.debug("SNS MessageId: " + publishResponse.getMessageId()); 37 | return publishResponse; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/main/java/com/sivalabs/demo/services/SQSService.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.sqs.AmazonSQSAsync; 4 | import com.amazonaws.services.sqs.model.CreateQueueRequest; 5 | import com.amazonaws.services.sqs.model.CreateQueueResult; 6 | import com.amazonaws.services.sqs.model.GetQueueAttributesRequest; 7 | import com.amazonaws.services.sqs.model.GetQueueAttributesResult; 8 | import com.amazonaws.services.sqs.model.ListQueuesResult; 9 | import com.amazonaws.services.sqs.model.Message; 10 | import com.amazonaws.services.sqs.model.SendMessageRequest; 11 | import lombok.RequiredArgsConstructor; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.util.List; 15 | 16 | import static java.util.Arrays.asList; 17 | 18 | @Service 19 | @RequiredArgsConstructor 20 | public class SQSService { 21 | private final AmazonSQSAsync amazonSQS; 22 | 23 | public CreateQueueResult createQueue(String queueName) { 24 | CreateQueueRequest createQueueRequest = new CreateQueueRequest(queueName) 25 | .addAttributesEntry("DelaySeconds", "60") 26 | .addAttributesEntry("MessageRetentionPeriod", "86400"); 27 | return amazonSQS.createQueue(createQueueRequest); 28 | } 29 | 30 | public ListQueuesResult listQueues() { 31 | return amazonSQS.listQueues(); 32 | } 33 | 34 | public String getQueueUrl(String queueName) { 35 | return amazonSQS.getQueueUrl(queueName).getQueueUrl(); 36 | } 37 | 38 | public GetQueueAttributesResult getQueueAttributes(String queueName) { 39 | String queueUrl = amazonSQS.getQueueUrl(queueName).getQueueUrl(); 40 | GetQueueAttributesRequest request = new GetQueueAttributesRequest(); 41 | request.setQueueUrl(queueUrl); 42 | request.setAttributeNames(asList("QueueArn", "VisibilityTimeout")); 43 | return amazonSQS.getQueueAttributes(request); 44 | } 45 | 46 | public void sendMessage(String queueName, String msg) { 47 | String queueUrl = amazonSQS.getQueueUrl(queueName).getQueueUrl(); 48 | 49 | SendMessageRequest sendMessageRequest = new SendMessageRequest() 50 | .withQueueUrl(queueUrl) 51 | .withMessageBody(msg) 52 | .withDelaySeconds(0); 53 | amazonSQS.sendMessage(sendMessageRequest); 54 | } 55 | 56 | public List readMessages(String queueName) { 57 | String queueUrl = amazonSQS.getQueueUrl(queueName).getQueueUrl(); 58 | return amazonSQS.receiveMessage(queueUrl).getMessages(); 59 | } 60 | 61 | public void deleteMessage(String queueName, Message message) { 62 | String queueUrl = amazonSQS.getQueueUrl(queueName).getQueueUrl(); 63 | amazonSQS.deleteMessage(queueUrl, message.getReceiptHandle()); 64 | } 65 | 66 | public void deleteQueue(String queueName) { 67 | String queueUrl = amazonSQS.getQueueUrl(queueName).getQueueUrl(); 68 | amazonSQS.deleteQueue(queueUrl); 69 | } 70 | 71 | } 72 | 73 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | localstack.enabled=true 2 | localstack.docker-image=localstack/localstack:2.0.0 3 | localstack.services=S3,SQS,SNS,DYNAMODB,DYNAMODBSTREAMS,KINESIS,IAM,SECRETSMANAGER,CLOUDWATCH,LAMBDA 4 | localstack.s3.enabled=true 5 | localstack.sqs.enabled=true 6 | localstack.sns.enabled=true 7 | localstack.dynamodb.enabled=true 8 | localstack.dynamodbstreams.enabled=true 9 | localstack.kinesis.enabled=true 10 | localstack.iam.enabled=true 11 | localstack.secretsmanager.enabled=true 12 | localstack.cloudwatch.enabled=true 13 | localstack.lambda.enabled=true 14 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/SpringbootLocalStackDemoApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo; 2 | 3 | import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsync; 4 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync; 5 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreamsAsync; 6 | import com.amazonaws.services.identitymanagement.AmazonIdentityManagementAsync; 7 | import com.amazonaws.services.kinesis.AmazonKinesisAsync; 8 | import com.amazonaws.services.lambda.AWSLambdaAsync; 9 | import com.amazonaws.services.s3.AmazonS3; 10 | import com.amazonaws.services.secretsmanager.AWSSecretsManagerAsync; 11 | import com.amazonaws.services.sns.AmazonSNSAsync; 12 | import com.amazonaws.services.sqs.AmazonSQSAsync; 13 | import io.github.sivalabs.localstack.LocalStackProperties; 14 | import org.junit.jupiter.api.Test; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.boot.test.context.SpringBootTest; 17 | import org.springframework.context.ApplicationContext; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | @SpringBootTest 22 | public class SpringbootLocalStackDemoApplicationTest { 23 | 24 | @Autowired 25 | private ApplicationContext context; 26 | 27 | @Test 28 | void shouldAutoConfigureLocalStackServices() { 29 | assertThat(context.getBean(LocalStackProperties.class)).isNotNull(); 30 | assertThat(context.getBean(AmazonS3.class)).isNotNull(); 31 | assertThat(context.getBean(AmazonSQSAsync.class)).isNotNull(); 32 | assertThat(context.getBean(AmazonSNSAsync.class)).isNotNull(); 33 | assertThat(context.getBean(AmazonDynamoDBAsync.class)).isNotNull(); 34 | assertThat(context.getBean(AmazonDynamoDBStreamsAsync.class)).isNotNull(); 35 | assertThat(context.getBean(AWSLambdaAsync.class)).isNotNull(); 36 | assertThat(context.getBean(AmazonKinesisAsync.class)).isNotNull(); 37 | assertThat(context.getBean(AmazonIdentityManagementAsync.class)).isNotNull(); 38 | assertThat(context.getBean(AWSSecretsManagerAsync.class)).isNotNull(); 39 | assertThat(context.getBean(AmazonCloudWatchAsync.class)).isNotNull(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/CloudWatchTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsync; 4 | import com.amazonaws.services.cloudwatch.model.Dimension; 5 | import com.amazonaws.services.cloudwatch.model.MetricDatum; 6 | import com.amazonaws.services.cloudwatch.model.PutMetricDataRequest; 7 | import com.amazonaws.services.cloudwatch.model.PutMetricDataResult; 8 | import com.amazonaws.services.cloudwatch.model.StandardUnit; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.junit.jupiter.api.Test; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | 14 | import static org.assertj.core.api.Assertions.assertThat; 15 | 16 | @SpringBootTest 17 | @Slf4j 18 | public class CloudWatchTest { 19 | 20 | @Autowired 21 | private AmazonCloudWatchAsync amazonCloudWatch; 22 | 23 | @Test 24 | void shouldWorkWithCloudWatch() { 25 | Dimension dimension = new Dimension() 26 | .withName("UNIQUE_PAGES") 27 | .withValue("URLS"); 28 | 29 | MetricDatum datum = new MetricDatum() 30 | .withMetricName("PAGES_VISITED") 31 | .withUnit(StandardUnit.None) 32 | .withValue(20.0) 33 | .withDimensions(dimension); 34 | 35 | PutMetricDataRequest request = new PutMetricDataRequest() 36 | .withNamespace("SITE/TRAFFIC") 37 | .withMetricData(datum); 38 | 39 | PutMetricDataResult response = amazonCloudWatch.putMetricData(request); 40 | assertThat(response).isNotNull(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/DynamoDBServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync; 4 | import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; 5 | import com.amazonaws.services.dynamodbv2.model.AttributeValue; 6 | import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; 7 | import com.amazonaws.services.dynamodbv2.model.CreateTableResult; 8 | import com.amazonaws.services.dynamodbv2.model.GetItemRequest; 9 | import com.amazonaws.services.dynamodbv2.model.GetItemResult; 10 | import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; 11 | import com.amazonaws.services.dynamodbv2.model.KeyType; 12 | import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; 13 | import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.junit.jupiter.api.Test; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.boot.test.context.SpringBootTest; 18 | 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | import static org.assertj.core.api.Assertions.assertThat; 24 | 25 | @SpringBootTest 26 | @Slf4j 27 | class DynamoDBServiceTest { 28 | 29 | private static final String tableName = "sample"; 30 | private static final String key = "Name"; 31 | 32 | @Autowired 33 | private DynamoDBService dynamoDBService; 34 | 35 | @Autowired 36 | private AmazonDynamoDBAsync amazonDynamoDBAsync; 37 | 38 | @Test 39 | void shouldGetTableNames() { 40 | List tables = dynamoDBService.listTables(); 41 | assertThat(tables).isNotNull(); 42 | } 43 | 44 | @Test 45 | void shouldBeAbleToDoBasicOperationsWithDynamoDB() { 46 | String name = "Siva"; 47 | createTable(); 48 | insertData(name); 49 | String result = readData(name); 50 | assertThat(result).isEqualTo(name); 51 | } 52 | 53 | private String readData(String name) { 54 | Map keyToGet = new HashMap<>(); 55 | keyToGet.put(key, new AttributeValue(name)); 56 | 57 | GetItemRequest request = new GetItemRequest().withKey(keyToGet).withTableName(tableName); 58 | GetItemResult outcome = amazonDynamoDBAsync.getItem(request); 59 | return outcome.getItem().get(key).getS(); 60 | } 61 | 62 | private void insertData(String name) { 63 | Map itemValues = new HashMap<>(); 64 | itemValues.put(key, new AttributeValue(name)); 65 | amazonDynamoDBAsync.putItem(tableName, itemValues); 66 | log.info("Item inserted into table: {} successfully", tableName); 67 | } 68 | 69 | private void createTable() { 70 | CreateTableRequest request = new CreateTableRequest() 71 | .withAttributeDefinitions(new AttributeDefinition(key, ScalarAttributeType.S)) 72 | .withKeySchema(new KeySchemaElement(key, KeyType.HASH)) 73 | .withProvisionedThroughput(new ProvisionedThroughput(10L, 10L)) 74 | .withTableName(tableName); 75 | CreateTableResult result = amazonDynamoDBAsync.createTable(request); 76 | log.info("Table with name : {} created successfully", result.getTableDescription().getTableName()); 77 | } 78 | } -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/DynamoDBStreamsTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync; 4 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreamsAsync; 5 | import com.amazonaws.services.dynamodbv2.model.AttributeAction; 6 | import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; 7 | import com.amazonaws.services.dynamodbv2.model.AttributeValue; 8 | import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate; 9 | import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; 10 | import com.amazonaws.services.dynamodbv2.model.DescribeStreamRequest; 11 | import com.amazonaws.services.dynamodbv2.model.DescribeStreamResult; 12 | import com.amazonaws.services.dynamodbv2.model.DescribeTableResult; 13 | import com.amazonaws.services.dynamodbv2.model.GetRecordsRequest; 14 | import com.amazonaws.services.dynamodbv2.model.GetRecordsResult; 15 | import com.amazonaws.services.dynamodbv2.model.GetShardIteratorRequest; 16 | import com.amazonaws.services.dynamodbv2.model.GetShardIteratorResult; 17 | import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; 18 | import com.amazonaws.services.dynamodbv2.model.KeyType; 19 | import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; 20 | import com.amazonaws.services.dynamodbv2.model.Record; 21 | import com.amazonaws.services.dynamodbv2.model.Shard; 22 | import com.amazonaws.services.dynamodbv2.model.ShardIteratorType; 23 | import com.amazonaws.services.dynamodbv2.model.StreamSpecification; 24 | import com.amazonaws.services.dynamodbv2.model.StreamViewType; 25 | import com.amazonaws.services.dynamodbv2.util.TableUtils; 26 | import lombok.extern.slf4j.Slf4j; 27 | import org.junit.jupiter.api.Test; 28 | import org.springframework.beans.factory.annotation.Autowired; 29 | import org.springframework.boot.test.context.SpringBootTest; 30 | 31 | import java.util.ArrayList; 32 | import java.util.Arrays; 33 | import java.util.HashMap; 34 | import java.util.List; 35 | import java.util.Map; 36 | 37 | @SpringBootTest 38 | @Slf4j 39 | class DynamoDBStreamsTest { 40 | 41 | @Autowired 42 | private AmazonDynamoDBAsync dynamoDBClient; 43 | 44 | @Autowired 45 | private AmazonDynamoDBStreamsAsync streamsClient; 46 | 47 | @Test 48 | void shouldBeAbleToDoBasicOperationsWithDynamoDB() { 49 | // Source: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.LowLevel.Walkthrough.html 50 | // Create a table, with a stream enabled 51 | String tableName = "TestTableForStreams"; 52 | 53 | ArrayList attributeDefinitions = new ArrayList<>( 54 | Arrays.asList(new AttributeDefinition() 55 | .withAttributeName("Id") 56 | .withAttributeType("N"))); 57 | 58 | ArrayList keySchema = new ArrayList<>( 59 | Arrays.asList(new KeySchemaElement() 60 | .withAttributeName("Id") 61 | .withKeyType(KeyType.HASH))); // Partition key 62 | 63 | StreamSpecification streamSpecification = new StreamSpecification() 64 | .withStreamEnabled(true) 65 | .withStreamViewType(StreamViewType.NEW_AND_OLD_IMAGES); 66 | 67 | CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName) 68 | .withKeySchema(keySchema).withAttributeDefinitions(attributeDefinitions) 69 | .withProvisionedThroughput(new ProvisionedThroughput() 70 | .withReadCapacityUnits(10L) 71 | .withWriteCapacityUnits(10L)) 72 | .withStreamSpecification(streamSpecification); 73 | 74 | System.out.println("Issuing CreateTable request for " + tableName); 75 | dynamoDBClient.createTable(createTableRequest); 76 | System.out.println("Waiting for " + tableName + " to be created..."); 77 | 78 | try { 79 | TableUtils.waitUntilActive(dynamoDBClient, tableName); 80 | } catch (Exception e) { 81 | throw new RuntimeException(e); 82 | } 83 | 84 | // Print the stream settings for the table 85 | DescribeTableResult describeTableResult = dynamoDBClient.describeTable(tableName); 86 | String streamArn = describeTableResult.getTable().getLatestStreamArn(); 87 | System.out.println("Current stream ARN for " + tableName + ": " + 88 | describeTableResult.getTable().getLatestStreamArn()); 89 | StreamSpecification streamSpec = describeTableResult.getTable().getStreamSpecification(); 90 | System.out.println("Stream enabled: " + streamSpec.getStreamEnabled()); 91 | System.out.println("Update view type: " + streamSpec.getStreamViewType()); 92 | System.out.println(); 93 | 94 | // Generate write activity in the table 95 | 96 | System.out.println("Performing write activities on " + tableName); 97 | int maxItemCount = 10; 98 | for (Integer i = 1; i <= maxItemCount; i++) { 99 | System.out.println("Processing item " + i + " of " + maxItemCount); 100 | 101 | // Write a new item 102 | Map item = new HashMap<>(); 103 | item.put("Id", new AttributeValue().withN(i.toString())); 104 | item.put("Message", new AttributeValue().withS("New item!")); 105 | dynamoDBClient.putItem(tableName, item); 106 | 107 | 108 | // Update the item 109 | Map key = new HashMap<>(); 110 | key.put("Id", new AttributeValue().withN(i.toString())); 111 | Map attributeUpdates = new HashMap<>(); 112 | attributeUpdates.put("Message", new AttributeValueUpdate() 113 | .withAction(AttributeAction.PUT) 114 | .withValue(new AttributeValue() 115 | .withS("This item has changed"))); 116 | dynamoDBClient.updateItem(tableName, key, attributeUpdates); 117 | 118 | // Delete the item 119 | dynamoDBClient.deleteItem(tableName, key); 120 | } 121 | 122 | // Get all the shard IDs from the stream. Note that DescribeStream returns 123 | // the shard IDs one page at a time. 124 | String lastEvaluatedShardId = null; 125 | 126 | do { 127 | DescribeStreamResult describeStreamResult = streamsClient.describeStream( 128 | new DescribeStreamRequest() 129 | .withStreamArn(streamArn) 130 | .withExclusiveStartShardId(lastEvaluatedShardId)); 131 | List shards = describeStreamResult.getStreamDescription().getShards(); 132 | 133 | // Process each shard on this page 134 | 135 | for (Shard shard : shards) { 136 | String shardId = shard.getShardId(); 137 | System.out.println("Shard: " + shard); 138 | 139 | // Get an iterator for the current shard 140 | 141 | GetShardIteratorRequest getShardIteratorRequest = new GetShardIteratorRequest() 142 | .withStreamArn(streamArn) 143 | .withShardId(shardId) 144 | .withShardIteratorType(ShardIteratorType.TRIM_HORIZON); 145 | GetShardIteratorResult getShardIteratorResult = 146 | streamsClient.getShardIterator(getShardIteratorRequest); 147 | String currentShardIter = getShardIteratorResult.getShardIterator(); 148 | 149 | // Shard iterator is not null until the Shard is sealed (marked as READ_ONLY). 150 | // To prevent running the loop until the Shard is sealed, which will be on average 151 | // 4 hours, we process only the items that were written into DynamoDB and then exit. 152 | int processedRecordCount = 0; 153 | while (currentShardIter != null && processedRecordCount < maxItemCount) { 154 | System.out.println(" Shard iterator: " + currentShardIter); 155 | 156 | // Use the shard iterator to read the stream records 157 | 158 | GetRecordsResult getRecordsResult = streamsClient.getRecords(new GetRecordsRequest() 159 | .withShardIterator(currentShardIter)); 160 | List records = getRecordsResult.getRecords(); 161 | for (Record record : records) { 162 | System.out.println(" " + record.getDynamodb()); 163 | } 164 | processedRecordCount += records.size(); 165 | currentShardIter = getRecordsResult.getNextShardIterator(); 166 | } 167 | } 168 | 169 | // If LastEvaluatedShardId is set, then there is 170 | // at least one more page of shard IDs to retrieve 171 | lastEvaluatedShardId = describeStreamResult.getStreamDescription().getLastEvaluatedShardId(); 172 | 173 | } while (lastEvaluatedShardId != null); 174 | 175 | // Delete the table 176 | System.out.println("Deleting the table..."); 177 | dynamoDBClient.deleteTable(tableName); 178 | 179 | System.out.println("Demo complete"); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/IAMTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.identitymanagement.AmazonIdentityManagementAsync; 4 | import com.amazonaws.services.identitymanagement.model.CreateGroupRequest; 5 | import com.amazonaws.services.identitymanagement.model.CreateGroupResult; 6 | import com.amazonaws.services.identitymanagement.model.GetGroupRequest; 7 | import com.amazonaws.services.identitymanagement.model.GetGroupResult; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | @SpringBootTest 16 | @Slf4j 17 | public class IAMTest { 18 | private static final String test_group = "test_group"; 19 | 20 | @Autowired 21 | private AmazonIdentityManagementAsync iamAsync; 22 | 23 | @Test 24 | void shouldWorkWithAWSIAM() { 25 | CreateGroupRequest request = new CreateGroupRequest().withGroupName(test_group); 26 | CreateGroupResult result = iamAsync.createGroup(request); 27 | assertThat(result).isNotNull(); 28 | 29 | GetGroupRequest getGroupRequest = new GetGroupRequest().withGroupName(test_group); 30 | GetGroupResult group = iamAsync.getGroup(getGroupRequest); 31 | assertThat(group).isNotNull(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/KinesisTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.kinesis.AmazonKinesisAsync; 4 | import com.amazonaws.services.kinesis.model.CreateStreamRequest; 5 | import com.amazonaws.services.kinesis.model.DescribeStreamRequest; 6 | import com.amazonaws.services.kinesis.model.DescribeStreamResult; 7 | import com.amazonaws.services.kinesis.model.ListStreamsRequest; 8 | import com.amazonaws.services.kinesis.model.ListStreamsResult; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.junit.jupiter.api.Test; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | 14 | import java.util.List; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | 19 | @SpringBootTest 20 | @Slf4j 21 | public class KinesisTest { 22 | private static final String streamName = "test_kinesis_stream"; 23 | 24 | @Autowired 25 | private AmazonKinesisAsync amazonKinesis; 26 | 27 | @Test 28 | void shouldCreateAndListKinesisStreams() throws Exception { 29 | createStream(streamName, 2); 30 | List streams = listStreams(); 31 | assertThat(streams).contains(streamName); 32 | } 33 | 34 | private List listStreams() { 35 | ListStreamsRequest listStreamsRequest = new ListStreamsRequest(); 36 | listStreamsRequest.setLimit(10); 37 | ListStreamsResult listStreamsResult = amazonKinesis.listStreams(listStreamsRequest); 38 | List streamNames = listStreamsResult.getStreamNames(); 39 | while (listStreamsResult.isHasMoreStreams()) { 40 | if (streamNames.size() > 0) { 41 | listStreamsRequest.setExclusiveStartStreamName(streamNames.get(streamNames.size() - 1)); 42 | } 43 | 44 | listStreamsResult = amazonKinesis.listStreams(listStreamsRequest); 45 | streamNames.addAll(listStreamsResult.getStreamNames()); 46 | } 47 | return streamNames; 48 | } 49 | 50 | private void createStream(String streamName, Integer streamSize) throws InterruptedException { 51 | // Create a stream. The number of shards determines the provisioned throughput. 52 | CreateStreamRequest createStreamRequest = new CreateStreamRequest(); 53 | createStreamRequest.setStreamName(streamName); 54 | createStreamRequest.setShardCount(streamSize); 55 | amazonKinesis.createStream(createStreamRequest); 56 | // The stream is now being created. Wait for it to become active. 57 | waitForStreamToBecomeAvailable(streamName); 58 | } 59 | 60 | private void waitForStreamToBecomeAvailable(String streamName) throws InterruptedException { 61 | System.out.printf("Waiting for %s to become ACTIVE...\n", streamName); 62 | 63 | long startTime = System.currentTimeMillis(); 64 | long endTime = startTime + TimeUnit.MINUTES.toMillis(20); 65 | while (System.currentTimeMillis() < endTime) { 66 | Thread.sleep(TimeUnit.SECONDS.toMillis(10)); 67 | 68 | try { 69 | DescribeStreamRequest describeStreamRequest = new DescribeStreamRequest(); 70 | describeStreamRequest.setStreamName(streamName); 71 | // ask for no more than 10 shards at a time -- this is an optional parameter 72 | describeStreamRequest.setLimit(10); 73 | DescribeStreamResult describeStreamResponse = amazonKinesis.describeStream(describeStreamRequest); 74 | 75 | String streamStatus = describeStreamResponse.getStreamDescription().getStreamStatus(); 76 | System.out.printf("\t- current state: %s\n", streamStatus); 77 | if ("ACTIVE".equals(streamStatus)) { 78 | return; 79 | } 80 | } catch (Exception e) { 81 | throw e; 82 | } 83 | } 84 | throw new RuntimeException(String.format("Stream %s never became active", streamName)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/LambdaTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.lambda.AWSLambdaAsync; 4 | import com.amazonaws.services.lambda.model.FunctionConfiguration; 5 | import com.amazonaws.services.lambda.model.ListFunctionsResult; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.junit.jupiter.api.Test; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | 11 | import java.util.List; 12 | 13 | @SpringBootTest 14 | @Slf4j 15 | public class LambdaTest { 16 | @Autowired 17 | private AWSLambdaAsync awsLambda; 18 | 19 | @Test 20 | void shouldWorkWithLambda() { 21 | ListFunctionsResult functionResult = awsLambda.listFunctions(); 22 | List functions = functionResult.getFunctions(); 23 | for (FunctionConfiguration config : functions) { 24 | System.out.println("The function name is "+config.getFunctionName()); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/S3ServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.s3.model.S3Object; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.jupiter.api.AfterEach; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | 11 | import java.io.ByteArrayInputStream; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | @SpringBootTest 16 | @Slf4j 17 | class S3ServiceTest { 18 | private static final String bucketName = "test-bucket-"+System.currentTimeMillis(); 19 | 20 | @Autowired 21 | private S3Service s3Service; 22 | 23 | @BeforeEach 24 | public void setUp() { 25 | s3Service.createBucket(bucketName); 26 | log.info("Created S3 Bucket: {}", bucketName); 27 | } 28 | 29 | @AfterEach 30 | public void tearDown() { 31 | s3Service.deleteBucketForce(bucketName); 32 | log.info("Deleted S3 Bucket: {}", bucketName); 33 | } 34 | 35 | @Test 36 | public void shouldStoreAndRetrieveDataFromS3Bucket() { 37 | s3Service.store(bucketName, "my-key-1", "my-value-1"); 38 | S3Object s3Object = s3Service.getObject(bucketName, "my-key-1"); 39 | assertThat(s3Object.getObjectContent()).hasSameContentAs(new ByteArrayInputStream("my-value-1".getBytes())); 40 | } 41 | } -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/SNSServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.sns.AmazonSNSAsync; 4 | import com.amazonaws.services.sns.model.CreateTopicResult; 5 | import com.amazonaws.services.sns.util.Topics; 6 | import com.amazonaws.services.sqs.AmazonSQSAsync; 7 | import com.amazonaws.services.sqs.model.Message; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.junit.jupiter.api.AfterEach; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | 15 | import java.util.List; 16 | 17 | import static java.util.concurrent.TimeUnit.SECONDS; 18 | import static org.assertj.core.api.Assertions.assertThat; 19 | import static org.awaitility.Awaitility.await; 20 | 21 | @SpringBootTest 22 | @Slf4j 23 | class SNSServiceTest { 24 | private static final String topicName = "test_topic_" + System.currentTimeMillis(); 25 | private static final String queueName = "test_queue_" + System.currentTimeMillis(); 26 | 27 | @Autowired 28 | private SNSService snsService; 29 | 30 | @Autowired 31 | private SQSService sqsService; 32 | 33 | @Autowired 34 | private AmazonSNSAsync amazonSNSAsync; 35 | 36 | @Autowired 37 | private AmazonSQSAsync amazonSQSAsync; 38 | 39 | @BeforeEach 40 | void setUp() { 41 | sqsService.createQueue(queueName); 42 | log.info("Created SQS Queue: {}", queueName); 43 | snsService.createTopic(topicName); 44 | log.info("Created SNS topic: {}", topicName); 45 | } 46 | 47 | @AfterEach 48 | void tearDown() { 49 | snsService.deleteTopic(topicName); 50 | log.info("Deleted SNS topic: {}", topicName); 51 | sqsService.deleteQueue(queueName); 52 | log.info("Deleted SQS Queue: {}", queueName); 53 | } 54 | 55 | @Test 56 | public void sendAndReceiveSNsMessage() { 57 | CreateTopicResult topic = snsService.createTopic(topicName); 58 | String sqsQueueUrl = sqsService.getQueueUrl(queueName); 59 | 60 | Topics.subscribeQueue(amazonSNSAsync, amazonSQSAsync, topic.getTopicArn(), sqsQueueUrl); 61 | 62 | snsService.sendMessage(topicName, "Test Message"); 63 | 64 | await().atMost(15, SECONDS).untilAsserted(() -> { 65 | List messages = sqsService.readMessages(queueName); 66 | //TODO; FIX IT - it should read 1 message 67 | assertThat(messages.size()).isEqualTo(0); 68 | }); 69 | } 70 | } -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/SQSServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.sqs.model.ListQueuesResult; 4 | import com.amazonaws.services.sqs.model.Message; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.junit.jupiter.api.AfterEach; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | import java.util.List; 13 | 14 | import static java.util.concurrent.TimeUnit.SECONDS; 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | import static org.awaitility.Awaitility.await; 17 | 18 | @SpringBootTest 19 | @Slf4j 20 | class SQSServiceTest { 21 | private static final String queueName = "test_queue_" + System.currentTimeMillis(); 22 | 23 | @Autowired 24 | private SQSService sqsService; 25 | 26 | @BeforeEach 27 | void setUp() { 28 | sqsService.createQueue(queueName); 29 | log.info("Created SQS Queue: {}", queueName); 30 | } 31 | 32 | @AfterEach 33 | void tearDown() { 34 | sqsService.deleteQueue(queueName); 35 | log.info("Deleted SQS Queue: {}", queueName); 36 | } 37 | 38 | @Test 39 | void shouldGetAllQueues() { 40 | ListQueuesResult listQueuesResult = sqsService.listQueues(); 41 | 42 | assertThat(listQueuesResult.getQueueUrls()).isNotEmpty(); 43 | 44 | for (String queueUrl : listQueuesResult.getQueueUrls()) { 45 | log.info("QueueUrl: {}", queueUrl); 46 | } 47 | } 48 | 49 | @Test 50 | public void sendAndReceiveSqsMessage() { 51 | sqsService.sendMessage(queueName, "Test Message"); 52 | 53 | await().atMost(15, SECONDS).untilAsserted(() -> { 54 | List messages = sqsService.readMessages(queueName); 55 | assertThat(messages.size()).isEqualTo(1); 56 | }); 57 | } 58 | } -------------------------------------------------------------------------------- /examples/localstack-spring-boot-sample/src/test/java/com/sivalabs/demo/services/SecretsManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.sivalabs.demo.services; 2 | 3 | import com.amazonaws.services.secretsmanager.AWSSecretsManagerAsync; 4 | import com.amazonaws.services.secretsmanager.model.CreateSecretRequest; 5 | import com.amazonaws.services.secretsmanager.model.CreateSecretResult; 6 | import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest; 7 | import com.amazonaws.services.secretsmanager.model.GetSecretValueResult; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | @SpringBootTest 16 | @Slf4j 17 | public class SecretsManagerTest { 18 | 19 | @Autowired 20 | private AWSSecretsManagerAsync secretsManagerAsync; 21 | 22 | @Test 23 | void shouldWorkWithSecretsManager() { 24 | CreateSecretRequest createSecretRequest = new CreateSecretRequest() 25 | .withName("db_password").withSecretString("secret"); 26 | CreateSecretResult secret = secretsManagerAsync.createSecret(createSecretRequest); 27 | 28 | GetSecretValueRequest getSecretRequest = new GetSecretValueRequest().withSecretId(secret.getARN()); 29 | GetSecretValueResult secretValue = secretsManagerAsync.getSecretValue(getSecretRequest); 30 | assertThat(secretValue.getName()).isEqualTo("db_password"); 31 | assertThat(secretValue.getSecretString()).isEqualTo("secret"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /maven-settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ossrh 6 | ${env.OSSRH_JIRA_USERNAME} 7 | ${env.OSSRH_JIRA_PASSWORD} 8 | 9 | 10 | 11 | 12 | 13 | ossrh 14 | 15 | true 16 | 17 | 18 | gpg 19 | ${env.GPG_KEY_NAME} 20 | ${env.GPG_PASSPHRASE} 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | io.github.sivalabs 5 | localstack-spring-boot-starter 6 | jar 7 | 0.0.4-SNAPSHOT 8 | localstack-spring-boot-starter 9 | SpringBoot starter for LocalStack 10 | https://github.com/sivalabs/localstack-spring-boot-starter 11 | 12 | 13 | 14 | MIT License 15 | http://www.opensource.org/licenses/mit-license.php 16 | 17 | 18 | 19 | 20 | 21 | author 22 | K Siva Prasad Reddy 23 | sivaprasadreddy.k@gmail.com 24 | 25 | 26 | 27 | 28 | scm:git:https://github.com/sivalabs/localstack-spring-boot-starter.git 29 | scm:git:https://github.com/sivalabs/localstack-spring-boot-starter.git 30 | https://github.com/sivalabs/localstack-spring-boot-starter 31 | HEAD 32 | 33 | 34 | 35 | ossrh 36 | https://oss.sonatype.org/content/repositories/snapshots 37 | 38 | 39 | ossrh 40 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 41 | 42 | 43 | 44 | 45 | UTF-8 46 | UTF-8 47 | 1.8 48 | 3.8.1 49 | 3.2.0 50 | 3.2.0 51 | 2.22.2 52 | 2.22.2 53 | 1.6 54 | 2.5.3 55 | 2.7.10 56 | 1.11.852 57 | 1.17.6 58 | 59 | 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-dependencies 65 | ${spring-boot.version} 66 | pom 67 | import 68 | 69 | 70 | org.testcontainers 71 | testcontainers-bom 72 | ${testcontainers.version} 73 | pom 74 | import 75 | 76 | 77 | com.amazonaws 78 | aws-java-sdk-bom 79 | ${aws-java-sdk.version} 80 | pom 81 | import 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-compiler-plugin 91 | ${maven-compiler-plugin.version} 92 | 93 | ${java.version} 94 | ${java.version} 95 | 96 | 97 | 98 | org.apache.maven.plugins 99 | maven-surefire-plugin 100 | ${maven-surefire-plugin.version} 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-failsafe-plugin 105 | ${maven-failsafe-plugin.version} 106 | 107 | 108 | org.apache.maven.plugins 109 | maven-source-plugin 110 | ${maven-source-plugin.version} 111 | 112 | 113 | attach-sources 114 | 115 | jar-no-fork 116 | 117 | 118 | 119 | 120 | 121 | org.apache.maven.plugins 122 | maven-javadoc-plugin 123 | ${maven-javadoc-plugin.version} 124 | true 125 | 126 | 8 127 | 128 | 129 | 130 | attach-javadocs 131 | 132 | jar 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | release 143 | 144 | 145 | 146 | org.apache.maven.plugins 147 | maven-gpg-plugin 148 | ${maven-gpg-plugin.version} 149 | 150 | 151 | sign-artifacts 152 | verify 153 | 154 | sign 155 | 156 | 157 | 158 | 159 | 160 | org.sonatype.plugins 161 | nexus-staging-maven-plugin 162 | 1.6.7 163 | true 164 | 165 | ossrh 166 | https://oss.sonatype.org/ 167 | true 168 | 169 | 170 | 171 | org.apache.maven.plugins 172 | maven-release-plugin 173 | ${maven-release-plugin.version} 174 | 175 | true 176 | false 177 | release 178 | deploy 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | org.projectlombok 189 | lombok 190 | true 191 | 192 | 193 | org.springframework.boot 194 | spring-boot-autoconfigure 195 | 196 | 197 | org.springframework.boot 198 | spring-boot-configuration-processor 199 | true 200 | 201 | 202 | org.testcontainers 203 | localstack 204 | 205 | 206 | com.amazonaws 207 | aws-java-sdk 208 | ${aws-java-sdk.version} 209 | provided 210 | 211 | 212 | 213 | org.springframework.boot 214 | spring-boot-starter-test 215 | test 216 | 217 | 218 | org.junit.vintage 219 | junit-vintage-engine 220 | 221 | 222 | 223 | 224 | org.testcontainers 225 | junit-jupiter 226 | test 227 | 228 | 229 | 230 | 231 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/EnableLocalStack.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack; 2 | 3 | import io.github.sivalabs.localstack.autoconfigure.LocalStackAutoConfiguration; 4 | import org.springframework.context.annotation.Import; 5 | 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | @Target(ElementType.TYPE) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | @Import(LocalStackAutoConfiguration.class) 16 | public @interface EnableLocalStack { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/LocalStackProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.testcontainers.containers.localstack.LocalStackContainer; 7 | 8 | import java.util.Collection; 9 | 10 | @ConfigurationProperties(prefix = LocalStackProperties.LOCALSTACK_PREFIX) 11 | @Setter 12 | @Getter 13 | public class LocalStackProperties { 14 | public static final String LOCALSTACK_PREFIX = "localstack"; 15 | public static final String BEAN_NAME_LOCALSTACK = "localStackBean"; 16 | public static final boolean ENABLE_SERVICE_BY_DEFAULT = true; 17 | 18 | private boolean enabled = true; 19 | private int edgePort = 4566; 20 | private String defaultRegion = "us-east-1"; 21 | private String hostname = "localhost"; 22 | private String hostnameExternal = "localhost"; 23 | private String dockerImage = "localstack/localstack:2.0.0"; 24 | private boolean useSsl = false; 25 | private Collection services; 26 | 27 | private S3Properties s3 = new S3Properties(); 28 | private SQSProperties sqs = new SQSProperties(); 29 | private SNSProperties sns = new SNSProperties(); 30 | private DynamoDBProperties dynamodb = new DynamoDBProperties(); 31 | private DynamoDBStreamsProperties dynamodbstreams = new DynamoDBStreamsProperties(); 32 | private KinesisProperties kinesis = new KinesisProperties(); 33 | private LambdaProperties lambda = new LambdaProperties(); 34 | private IamProperties iam = new IamProperties(); 35 | private SecretsManagerProperties secretsmanager = new SecretsManagerProperties(); 36 | private CloudWatchProperties cloudwatch = new CloudWatchProperties(); 37 | 38 | @Setter 39 | @Getter 40 | public static class S3Properties extends CommonProperties { 41 | } 42 | 43 | @Setter 44 | @Getter 45 | public static class SQSProperties extends CommonProperties { 46 | } 47 | 48 | @Setter 49 | @Getter 50 | public static class SNSProperties extends CommonProperties { 51 | } 52 | 53 | @Setter 54 | @Getter 55 | public static class DynamoDBProperties extends CommonProperties { 56 | } 57 | 58 | @Setter 59 | @Getter 60 | public static class DynamoDBStreamsProperties extends CommonProperties { 61 | } 62 | 63 | @Setter 64 | @Getter 65 | public static class KinesisProperties extends CommonProperties { 66 | } 67 | 68 | @Setter 69 | @Getter 70 | public static class LambdaProperties extends CommonProperties { 71 | } 72 | 73 | @Setter 74 | @Getter 75 | public static class IamProperties extends CommonProperties { 76 | } 77 | 78 | @Setter 79 | @Getter 80 | public static class SecretsManagerProperties extends CommonProperties { 81 | } 82 | 83 | @Setter 84 | @Getter 85 | public static class CloudWatchProperties extends CommonProperties { 86 | } 87 | 88 | @Setter 89 | @Getter 90 | private static class CommonProperties { 91 | private boolean enabled = ENABLE_SERVICE_BY_DEFAULT; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/LocalStackAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure; 2 | 3 | import io.github.sivalabs.localstack.autoconfigure.configurator.AWSLambdaConfiguration; 4 | import io.github.sivalabs.localstack.autoconfigure.configurator.AWSSecretsManagerConfiguration; 5 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonCloudWatchConfiguration; 6 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonDynamoDBConfiguration; 7 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonDynamoDBStreamsConfiguration; 8 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonIAMConfiguration; 9 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonKinesisConfiguration; 10 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonS3Configuration; 11 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonSNSConfiguration; 12 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonSQSConfiguration; 13 | import io.github.sivalabs.localstack.autoconfigure.configurator.LocalStackContainerConfiguration; 14 | import org.springframework.boot.autoconfigure.AutoConfigureOrder; 15 | import org.springframework.context.annotation.Configuration; 16 | import org.springframework.context.annotation.Import; 17 | import org.springframework.core.Ordered; 18 | 19 | @Configuration 20 | @AutoConfigureOrder(value = Ordered.HIGHEST_PRECEDENCE) 21 | @Import({ 22 | LocalStackContainerConfiguration.class, 23 | AmazonS3Configuration.class, 24 | AmazonSQSConfiguration.class, 25 | AmazonSNSConfiguration.class, 26 | AmazonDynamoDBConfiguration.class, 27 | AmazonDynamoDBStreamsConfiguration.class, 28 | AmazonCloudWatchConfiguration.class, 29 | AWSLambdaConfiguration.class, 30 | AWSSecretsManagerConfiguration.class, 31 | AmazonKinesisConfiguration.class, 32 | AmazonIAMConfiguration.class 33 | }) 34 | public class LocalStackAutoConfiguration { 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AWSLambdaConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.lambda.AWSLambdaAsync; 4 | import com.amazonaws.services.lambda.AWSLambdaAsyncClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.LAMBDA; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.lambda.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AWSLambdaAsync.class) 17 | public class AWSLambdaConfiguration extends AbstractAmazonClient { 18 | public AWSLambdaConfiguration(LocalStackContainer localStackContainer) { 19 | super(localStackContainer); 20 | } 21 | 22 | @Bean 23 | @Primary 24 | public AWSLambdaAsync awsLambdaAsyncLocalStack() { 25 | return AWSLambdaAsyncClientBuilder.standard() 26 | .withEndpointConfiguration(getEndpointConfiguration(LAMBDA)) 27 | .withCredentials(getCredentialsProvider()) 28 | .build(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AWSSecretsManagerConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.secretsmanager.AWSSecretsManagerAsync; 4 | import com.amazonaws.services.secretsmanager.AWSSecretsManagerAsyncClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SECRETSMANAGER; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.secretsmanager.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AWSSecretsManagerAsync.class) 17 | public class AWSSecretsManagerConfiguration extends AbstractAmazonClient { 18 | public AWSSecretsManagerConfiguration(LocalStackContainer localStackContainer) { 19 | super(localStackContainer); 20 | } 21 | 22 | @Bean 23 | @Primary 24 | public AWSSecretsManagerAsync awsSecretsManagerAsyncLocalStack() { 25 | return AWSSecretsManagerAsyncClientBuilder.standard() 26 | .withEndpointConfiguration(getEndpointConfiguration(SECRETSMANAGER)) 27 | .withCredentials(getCredentialsProvider()) 28 | .build(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AbstractAmazonClient.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.auth.AWSCredentialsProvider; 4 | import com.amazonaws.auth.AWSStaticCredentialsProvider; 5 | import com.amazonaws.auth.BasicAWSCredentials; 6 | import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration; 7 | import lombok.RequiredArgsConstructor; 8 | import org.testcontainers.containers.localstack.LocalStackContainer; 9 | import org.testcontainers.containers.localstack.LocalStackContainer.Service; 10 | 11 | @RequiredArgsConstructor 12 | public abstract class AbstractAmazonClient { 13 | protected final LocalStackContainer localStackContainer; 14 | 15 | protected EndpointConfiguration getEndpointConfiguration(Service service) { 16 | return localStackContainer.getEndpointConfiguration(service); 17 | } 18 | 19 | protected AWSCredentialsProvider getCredentialsProvider() { 20 | return new AWSStaticCredentialsProvider( 21 | new BasicAWSCredentials(localStackContainer.getAccessKey(), localStackContainer.getSecretKey()) 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AmazonCloudWatchConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsync; 4 | import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsyncClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.CLOUDWATCH; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.cloudwatch.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AmazonCloudWatchAsync.class) 17 | public class AmazonCloudWatchConfiguration extends AbstractAmazonClient { 18 | public AmazonCloudWatchConfiguration(LocalStackContainer localStackContainer) { 19 | super(localStackContainer); 20 | } 21 | 22 | @Bean 23 | @Primary 24 | public AmazonCloudWatchAsync amazonCloudWatchAsyncLocalStack() { 25 | return AmazonCloudWatchAsyncClientBuilder.standard() 26 | .withEndpointConfiguration(getEndpointConfiguration(CLOUDWATCH)) 27 | .withCredentials(getCredentialsProvider()) 28 | .build(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AmazonDynamoDBConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync; 4 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsyncClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.DYNAMODB; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.dynamodb.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AmazonDynamoDBAsync.class) 17 | public class AmazonDynamoDBConfiguration extends AbstractAmazonClient { 18 | 19 | public AmazonDynamoDBConfiguration(LocalStackContainer localStackContainer) { 20 | super(localStackContainer); 21 | } 22 | 23 | @Bean 24 | @Primary 25 | public AmazonDynamoDBAsync amazonDynamoDBAsyncLocalStack() { 26 | return AmazonDynamoDBAsyncClientBuilder.standard() 27 | .withEndpointConfiguration(getEndpointConfiguration(DYNAMODB)) 28 | .withCredentials(getCredentialsProvider()) 29 | .build(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AmazonDynamoDBStreamsConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreamsAsync; 4 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreamsAsyncClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.DYNAMODB_STREAMS; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.dynamodbstreams.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AmazonDynamoDBStreamsAsync.class) 17 | public class AmazonDynamoDBStreamsConfiguration extends AbstractAmazonClient { 18 | 19 | public AmazonDynamoDBStreamsConfiguration(LocalStackContainer localStackContainer) { 20 | super(localStackContainer); 21 | } 22 | 23 | @Bean 24 | @Primary 25 | public AmazonDynamoDBStreamsAsync amazonDynamoDBStreamsAsyncLocalStack() { 26 | return AmazonDynamoDBStreamsAsyncClientBuilder.standard() 27 | .withEndpointConfiguration(getEndpointConfiguration(DYNAMODB_STREAMS)) 28 | .withCredentials(getCredentialsProvider()) 29 | .build(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AmazonIAMConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.identitymanagement.AmazonIdentityManagementAsync; 4 | import com.amazonaws.services.identitymanagement.AmazonIdentityManagementAsyncClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.IAM; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.iam.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AmazonIdentityManagementAsync.class) 17 | public class AmazonIAMConfiguration extends AbstractAmazonClient { 18 | 19 | public AmazonIAMConfiguration(LocalStackContainer localStackContainer) { 20 | super(localStackContainer); 21 | } 22 | 23 | @Bean 24 | @Primary 25 | public AmazonIdentityManagementAsync amazonIdentityManagementAsyncLocalStack() { 26 | return AmazonIdentityManagementAsyncClientBuilder.standard() 27 | .withEndpointConfiguration(getEndpointConfiguration(IAM)) 28 | .withCredentials(getCredentialsProvider()) 29 | .build(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AmazonKinesisConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.kinesis.AmazonKinesisAsync; 4 | import com.amazonaws.services.kinesis.AmazonKinesisAsyncClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.KINESIS; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.kinesis.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AmazonKinesisAsync.class) 17 | public class AmazonKinesisConfiguration extends AbstractAmazonClient { 18 | 19 | public AmazonKinesisConfiguration(LocalStackContainer localStackContainer) { 20 | super(localStackContainer); 21 | } 22 | 23 | @Bean 24 | @Primary 25 | public AmazonKinesisAsync amazonKinesisAsyncLocalStack() { 26 | return AmazonKinesisAsyncClientBuilder.standard() 27 | .withEndpointConfiguration(getEndpointConfiguration(KINESIS)) 28 | .withCredentials(getCredentialsProvider()) 29 | .build(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AmazonS3Configuration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.s3.AmazonS3; 4 | import com.amazonaws.services.s3.AmazonS3ClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.s3.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AmazonS3.class) 17 | public class AmazonS3Configuration extends AbstractAmazonClient { 18 | 19 | public AmazonS3Configuration(LocalStackContainer localStackContainer) { 20 | super(localStackContainer); 21 | } 22 | 23 | @Bean 24 | @Primary 25 | public AmazonS3 amazonS3LocalStack() { 26 | return AmazonS3ClientBuilder.standard() 27 | .withEndpointConfiguration(getEndpointConfiguration(S3)) 28 | .withCredentials(getCredentialsProvider()) 29 | .enablePathStyleAccess() 30 | .build(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AmazonSNSConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.sns.AmazonSNSAsync; 4 | import com.amazonaws.services.sns.AmazonSNSAsyncClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SNS; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.sns.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AmazonSNSAsync.class) 17 | public class AmazonSNSConfiguration extends AbstractAmazonClient { 18 | 19 | public AmazonSNSConfiguration(LocalStackContainer localStackContainer) { 20 | super(localStackContainer); 21 | } 22 | 23 | @Primary 24 | @Bean 25 | public AmazonSNSAsync amazonSNSAsyncLocalStack() { 26 | return AmazonSNSAsyncClientBuilder.standard() 27 | .withEndpointConfiguration(getEndpointConfiguration(SNS)) 28 | .withCredentials(getCredentialsProvider()) 29 | .build(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/AmazonSQSConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import com.amazonaws.services.sqs.AmazonSQSAsync; 4 | import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Primary; 9 | import org.testcontainers.containers.localstack.LocalStackContainer; 10 | 11 | import static io.github.sivalabs.localstack.LocalStackProperties.ENABLE_SERVICE_BY_DEFAULT; 12 | import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; 13 | 14 | @ConditionalOnLocalStackService 15 | @ConditionalOnProperty(name = "localstack.sqs.enabled", havingValue = "true", matchIfMissing = ENABLE_SERVICE_BY_DEFAULT) 16 | @ConditionalOnClass(AmazonSQSAsync.class) 17 | public class AmazonSQSConfiguration extends AbstractAmazonClient { 18 | 19 | public AmazonSQSConfiguration(LocalStackContainer localStackContainer) { 20 | super(localStackContainer); 21 | } 22 | 23 | @Bean 24 | @Primary 25 | public AmazonSQSAsync amazonSQSAsyncLocalStack() { 26 | return AmazonSQSAsyncClientBuilder.standard() 27 | .withEndpointConfiguration(getEndpointConfiguration(SQS)) 28 | .withCredentials(getCredentialsProvider()) 29 | .build(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/ConditionalOnLocalStackService.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | import java.lang.annotation.Documented; 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | import static io.github.sivalabs.localstack.LocalStackProperties.BEAN_NAME_LOCALSTACK; 14 | 15 | @Target(ElementType.TYPE) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Documented 18 | @Configuration 19 | @ConditionalOnExpression("${localstack.enabled:true}") 20 | @AutoConfigureAfter(name = BEAN_NAME_LOCALSTACK) 21 | public @interface ConditionalOnLocalStackService { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/github/sivalabs/localstack/autoconfigure/configurator/LocalStackContainerConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure.configurator; 2 | 3 | import io.github.sivalabs.localstack.LocalStackProperties; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 9 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.core.env.ConfigurableEnvironment; 13 | import org.springframework.core.env.MapPropertySource; 14 | import org.testcontainers.containers.localstack.LocalStackContainer; 15 | 16 | import java.util.LinkedHashMap; 17 | import java.util.Map; 18 | 19 | import static io.github.sivalabs.localstack.LocalStackProperties.BEAN_NAME_LOCALSTACK; 20 | 21 | @Configuration 22 | @ConditionalOnClass({LocalStackContainer.class}) 23 | @ConditionalOnProperty(name = "localstack.enabled", matchIfMissing = true) 24 | @EnableConfigurationProperties(LocalStackProperties.class) 25 | public class LocalStackContainerConfiguration { 26 | 27 | private static final Logger log = LoggerFactory.getLogger(LocalStackContainerConfiguration.class); 28 | 29 | @ConditionalOnMissingBean(name = BEAN_NAME_LOCALSTACK) 30 | @Bean(name = BEAN_NAME_LOCALSTACK, destroyMethod = "stop") 31 | public LocalStackContainer localStack(ConfigurableEnvironment environment, 32 | LocalStackProperties properties) { 33 | log.info("Starting Localstack server. Docker image: {}", properties.getDockerImage()); 34 | 35 | LocalStackContainer localStackContainer = new EmbeddedLocalStackContainer(properties.getDockerImage()); 36 | localStackContainer.withEnv("EDGE_PORT", String.valueOf(properties.getEdgePort())) 37 | .withEnv("DEFAULT_REGION", properties.getDefaultRegion()) 38 | .withEnv("HOSTNAME", properties.getHostname()) 39 | .withEnv("HOSTNAME_EXTERNAL", properties.getHostnameExternal()) 40 | .withEnv("USE_SSL", String.valueOf(properties.isUseSsl())); 41 | 42 | for (LocalStackContainer.Service service : properties.getServices()) { 43 | localStackContainer.withServices(service); 44 | } 45 | localStackContainer.start(); 46 | registerLocalStackEnvironment(localStackContainer, environment, properties); 47 | return localStackContainer; 48 | } 49 | 50 | private void registerLocalStackEnvironment(LocalStackContainer localStack, 51 | ConfigurableEnvironment environment, 52 | LocalStackProperties properties) { 53 | String host = localStack.getContainerIpAddress(); 54 | 55 | Map map = new LinkedHashMap<>(); 56 | map.put("localstack.host", host); 57 | map.put("localstack.accessKey", localStack.getAccessKey()); 58 | map.put("localstack.secretKey", localStack.getSecretKey()); 59 | map.put("localstack.region", localStack.getRegion()); 60 | String prefix = "localstack."; 61 | for (LocalStackContainer.Service service : properties.getServices()) { 62 | map.put(prefix + service + ".endpoint", localStack.getEndpointConfiguration(service).getServiceEndpoint()); 63 | } 64 | log.info("Started Localstack. Connection details: {}", map); 65 | 66 | MapPropertySource propertySource = new MapPropertySource("localstackInfo", map); 67 | environment.getPropertySources().addFirst(propertySource); 68 | setSystemProperties(localStack); 69 | } 70 | 71 | private static void setSystemProperties(LocalStackContainer localStack) { 72 | System.setProperty("aws.accessKeyId", localStack.getAccessKey()); 73 | System.setProperty("aws.secretKey", localStack.getAccessKey()); 74 | System.setProperty("com.amazonaws.sdk.disableCbor", "true"); 75 | System.setProperty("com.amazonaws.sdk.disableCertChecking", "true"); 76 | } 77 | 78 | private static class EmbeddedLocalStackContainer extends LocalStackContainer { 79 | EmbeddedLocalStackContainer(final String dockerImageName) { 80 | setDockerImageName(dockerImageName); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/io/github/sivalabs/localstack/autoconfigure/LocalStackAutoConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package io.github.sivalabs.localstack.autoconfigure; 2 | 3 | import com.amazonaws.services.cloudwatch.AmazonCloudWatchAsync; 4 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBAsync; 5 | import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreamsAsync; 6 | import com.amazonaws.services.identitymanagement.AmazonIdentityManagementAsync; 7 | import com.amazonaws.services.kinesis.AmazonKinesisAsync; 8 | import com.amazonaws.services.lambda.AWSLambdaAsync; 9 | import com.amazonaws.services.s3.AmazonS3; 10 | import com.amazonaws.services.secretsmanager.AWSSecretsManagerAsync; 11 | import com.amazonaws.services.sns.AmazonSNSAsync; 12 | import com.amazonaws.services.sqs.AmazonSQSAsync; 13 | import io.github.sivalabs.localstack.LocalStackProperties; 14 | import io.github.sivalabs.localstack.autoconfigure.configurator.AWSLambdaConfiguration; 15 | import io.github.sivalabs.localstack.autoconfigure.configurator.AWSSecretsManagerConfiguration; 16 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonCloudWatchConfiguration; 17 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonDynamoDBConfiguration; 18 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonIAMConfiguration; 19 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonKinesisConfiguration; 20 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonS3Configuration; 21 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonSNSConfiguration; 22 | import io.github.sivalabs.localstack.autoconfigure.configurator.AmazonSQSConfiguration; 23 | import org.junit.jupiter.api.Test; 24 | import org.springframework.boot.autoconfigure.AutoConfigurations; 25 | import org.springframework.boot.test.context.runner.ApplicationContextRunner; 26 | 27 | import static org.assertj.core.api.Assertions.assertThat; 28 | 29 | public class LocalStackAutoConfigurationTest { 30 | 31 | private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() 32 | .withConfiguration(AutoConfigurations.of( 33 | LocalStackAutoConfiguration.class, 34 | AmazonS3Configuration.class, 35 | AmazonSQSConfiguration.class, 36 | AmazonSNSConfiguration.class, 37 | AmazonDynamoDBConfiguration.class, 38 | AmazonCloudWatchConfiguration.class, 39 | AmazonIAMConfiguration.class, 40 | AmazonKinesisConfiguration.class, 41 | AWSLambdaConfiguration.class, 42 | AWSSecretsManagerConfiguration.class 43 | )); 44 | 45 | @Test 46 | public void shouldAutoConfigureAWSServiceClientsBasedOnDefaultConfiguration() { 47 | this.contextRunner 48 | .withPropertyValues( 49 | "localstack.services=SQS,S3,SNS,DYNAMODB,DYNAMODBSTREAMS,KINESIS,IAM,LAMBDA,CLOUDWATCH,SECRETSMANAGER" 50 | ) 51 | .run((context) -> { 52 | assertThat(context).hasSingleBean(LocalStackProperties.class); 53 | LocalStackProperties properties = context.getBean(LocalStackProperties.class); 54 | assertThat(properties.isEnabled()).isTrue(); 55 | 56 | assertThat(context).hasSingleBean(AmazonS3.class); 57 | assertThat(context).hasSingleBean(AmazonSQSAsync.class); 58 | assertThat(context).hasSingleBean(AmazonSNSAsync.class); 59 | assertThat(context).hasSingleBean(AmazonDynamoDBAsync.class); 60 | assertThat(context).hasSingleBean(AmazonDynamoDBStreamsAsync.class); 61 | assertThat(context).hasSingleBean(AmazonKinesisAsync.class); 62 | assertThat(context).hasSingleBean(AmazonIdentityManagementAsync.class); 63 | assertThat(context).hasSingleBean(AWSSecretsManagerAsync.class); 64 | assertThat(context).hasSingleBean(AmazonCloudWatchAsync.class); 65 | assertThat(context).hasSingleBean(AWSLambdaAsync.class); 66 | }); 67 | } 68 | 69 | @Test 70 | public void shouldAutoConfigureAllEnabledAWSServiceClients() { 71 | this.contextRunner 72 | .withPropertyValues( 73 | "localstack.services=SQS,S3,SNS,DYNAMODB,DYNAMODBSTREAMS,KINESIS,IAM,LAMBDA,CLOUDWATCH,SECRETSMANAGER", 74 | "localstack.s3.enabled=true", 75 | "localstack.sqs.enabled=true", 76 | "localstack.sns.enabled=true", 77 | "localstack.dynamodb.enabled=true", 78 | "localstack.dynamodbstreams.enabled=true", 79 | "localstack.kinesis.enabled=true", 80 | "localstack.iam.enabled=true", 81 | "localstack.lambda.enabled=true", 82 | "localstack.cloudwatch.enabled=true", 83 | "localstack.secretsmanager.enabled=true" 84 | ) 85 | .run((context) -> { 86 | assertThat(context).hasSingleBean(LocalStackProperties.class); 87 | LocalStackProperties properties = context.getBean(LocalStackProperties.class); 88 | assertThat(properties.isEnabled()).isTrue(); 89 | 90 | assertThat(context).hasSingleBean(AmazonS3.class); 91 | assertThat(context).hasSingleBean(AmazonSQSAsync.class); 92 | assertThat(context).hasSingleBean(AmazonSNSAsync.class); 93 | assertThat(context).hasSingleBean(AmazonDynamoDBAsync.class); 94 | assertThat(context).hasSingleBean(AmazonDynamoDBStreamsAsync.class); 95 | assertThat(context).hasSingleBean(AmazonKinesisAsync.class); 96 | assertThat(context).hasSingleBean(AmazonIdentityManagementAsync.class); 97 | assertThat(context).hasSingleBean(AWSSecretsManagerAsync.class); 98 | assertThat(context).hasSingleBean(AmazonCloudWatchAsync.class); 99 | assertThat(context).hasSingleBean(AWSLambdaAsync.class); 100 | }); 101 | } 102 | 103 | @Test 104 | public void shouldAutoConfigureOnlyEnabledAWSServiceClients() { 105 | this.contextRunner 106 | .withPropertyValues( 107 | "localstack.services=SQS,S3", 108 | "localstack.s3.enabled=true", 109 | "localstack.sqs.enabled=true", 110 | "localstack.sns.enabled=true", 111 | "localstack.dynamodb.enabled=false", 112 | "localstack.dynamodbstreams.enabled=false", 113 | "localstack.kinesis.enabled=false", 114 | "localstack.iam.enabled=false", 115 | "localstack.lambda.enabled=false", 116 | "localstack.cloudwatch.enabled=false", 117 | "localstack.secretsmanager.enabled=false" 118 | ) 119 | .run((context) -> { 120 | assertThat(context).hasSingleBean(LocalStackProperties.class); 121 | assertThat(context).hasSingleBean(AmazonS3.class); 122 | assertThat(context).hasSingleBean(AmazonSQSAsync.class); 123 | assertThat(context).hasSingleBean(AmazonSNSAsync.class); 124 | 125 | assertThat(context).doesNotHaveBean(AmazonDynamoDBAsync.class); 126 | assertThat(context).doesNotHaveBean(AmazonDynamoDBStreamsAsync.class); 127 | assertThat(context).doesNotHaveBean(AmazonKinesisAsync.class); 128 | assertThat(context).doesNotHaveBean(AmazonIdentityManagementAsync.class); 129 | assertThat(context).doesNotHaveBean(AWSSecretsManagerAsync.class); 130 | assertThat(context).doesNotHaveBean(AmazonCloudWatchAsync.class); 131 | assertThat(context).doesNotHaveBean(AWSLambdaAsync.class); 132 | }); 133 | } 134 | 135 | @Test 136 | public void shouldNotAutoConfigureAWSServiceClientsWhenLocalStackInNotEnabled() { 137 | this.contextRunner 138 | .withPropertyValues( 139 | "localstack.enabled=false", 140 | "localstack.services=SQS,S3,SNS,DYNAMODB,DYNAMODBSTREAMS,KINESIS,IAM,LAMBDA,CLOUDWATCH,SECRETSMANAGER", 141 | "localstack.s3.enabled=true", 142 | "localstack.sqs.enabled=true", 143 | "localstack.sns.enabled=true", 144 | "localstack.dynamodb.enabled=true", 145 | "localstack.dynamodbstreams.enabled=true", 146 | "localstack.kinesis.enabled=true", 147 | "localstack.iam.enabled=true", 148 | "localstack.lambda.enabled=true", 149 | "localstack.cloudwatch.enabled=true", 150 | "localstack.secretsmanager.enabled=true" 151 | ) 152 | .run((context) -> { 153 | assertThat(context).doesNotHaveBean(LocalStackProperties.class); 154 | assertThat(context).doesNotHaveBean(AmazonS3.class); 155 | assertThat(context).doesNotHaveBean(AmazonSQSAsync.class); 156 | assertThat(context).doesNotHaveBean(AmazonSNSAsync.class); 157 | assertThat(context).doesNotHaveBean(AmazonDynamoDBAsync.class); 158 | assertThat(context).doesNotHaveBean(AmazonDynamoDBStreamsAsync.class); 159 | assertThat(context).doesNotHaveBean(AmazonKinesisAsync.class); 160 | assertThat(context).doesNotHaveBean(AmazonIdentityManagementAsync.class); 161 | assertThat(context).doesNotHaveBean(AWSSecretsManagerAsync.class); 162 | assertThat(context).doesNotHaveBean(AmazonCloudWatchAsync.class); 163 | assertThat(context).doesNotHaveBean(AWSLambdaAsync.class); 164 | }); 165 | } 166 | } 167 | --------------------------------------------------------------------------------