├── .sdkmanrc ├── cloudwatch ├── src │ ├── test │ │ ├── resources │ │ │ ├── application.properties │ │ │ └── logback-test.xml │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── cloudwatch │ │ │ └── CloudwatchApplicationTests.java │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── com │ │ └── example │ │ └── cloudwatch │ │ ├── CloudwatchApplication.java │ │ └── GreetingController.java └── pom.xml ├── s3 ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── s3 │ │ │ └── S3Application.java │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── s3 │ │ └── S3ApplicationTests.java └── pom.xml ├── ses ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── ses │ │ │ └── SesApplication.java │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── ses │ │ └── SesApplicationTests.java └── pom.xml ├── sqs ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── sqs │ │ │ └── SqsApplication.java │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── sqs │ │ └── SqsApplicationTests.java └── pom.xml ├── cognito ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── cognito │ │ │ ├── CognitoApplication.java │ │ │ ├── ExampleSecurity.java │ │ │ └── ExampleController.java │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── cognito │ │ └── CognitoApplicationTests.java └── pom.xml ├── parameterstore ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── parameterstore │ │ │ ├── ParameterstoreApplication.java │ │ │ └── GreetingController.java │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── parameterstore │ │ └── ParameterstoreApplicationTests.java └── pom.xml ├── secretsmanager ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── secretsmanager │ │ │ ├── SecretsmanagerApplication.java │ │ │ └── GreetingController.java │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── secretsmanager │ │ └── SecretsmanagerApplicationTests.java └── pom.xml ├── snowflake ├── src │ ├── main │ │ ├── resources │ │ │ └── db │ │ │ │ └── migration │ │ │ │ └── V1__setup.sql │ │ └── java │ │ │ └── org │ │ │ └── example │ │ │ └── snowflake │ │ │ └── SpringBootSnowflakeApplication.java │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── org │ │ └── example │ │ └── snowflake │ │ └── SnowflakeTest.java └── pom.xml ├── lambda ├── src │ ├── main │ │ ├── resources │ │ │ └── db │ │ │ │ └── migration │ │ │ │ └── V1__create_populate_table.sql │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── springcloudfunctionaws │ │ │ └── SpringCloudFunctionAwsApplication.java │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── springcloudfunctionaws │ │ └── SpringCloudFunctionAwsApplicationTests.java └── pom.xml ├── .gitignore ├── mq-broker ├── src │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── mqbroker │ │ └── MqBrokerTests.java └── pom.xml ├── api-gateway ├── src │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── apigateway │ │ └── ApiGatewayTests.java └── pom.xml ├── step-functions ├── src │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── stepfunctions │ │ └── StepFunctionsTests.java └── pom.xml ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── LICENSE ├── pom.xml ├── .github └── workflows │ └── ci.yml ├── mvnw.cmd └── mvnw /.sdkmanrc: -------------------------------------------------------------------------------- 1 | java=25-tem 2 | -------------------------------------------------------------------------------- /cloudwatch/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /s3/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ses/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sqs/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /cloudwatch/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /cognito/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /parameterstore/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /secretsmanager/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /snowflake/src/main/resources/db/migration/V1__setup.sql: -------------------------------------------------------------------------------- 1 | create table profile (name VARCHAR(25) NOT NULL PRIMARY KEY); 2 | 3 | insert into profile (name) values ('test'); -------------------------------------------------------------------------------- /lambda/src/main/resources/db/migration/V1__create_populate_table.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS profile(id serial primary key, name varchar(255) not null); 2 | 3 | INSERT INTO profile (name) VALUES ('profile-1'); 4 | INSERT INTO profile (name) VALUES ('profile-2'); 5 | INSERT INTO profile (name) VALUES ('profile-3'); 6 | INSERT INTO profile (name) VALUES ('profile-4'); -------------------------------------------------------------------------------- /s3/src/main/java/com/example/s3/S3Application.java: -------------------------------------------------------------------------------- 1 | package com.example.s3; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class S3Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(S3Application.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ses/src/main/java/com/example/ses/SesApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.ses; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SesApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /sqs/src/main/java/com/example/sqs/SqsApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.sqs; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SqsApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SqsApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /cognito/src/main/java/com/example/cognito/CognitoApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.cognito; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CognitoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CognitoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /cloudwatch/src/main/java/com/example/cloudwatch/CloudwatchApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.cloudwatch; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CloudwatchApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CloudwatchApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /parameterstore/src/main/java/com/example/parameterstore/ParameterstoreApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.parameterstore; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ParameterstoreApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ParameterstoreApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /secretsmanager/src/main/java/com/example/secretsmanager/SecretsmanagerApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.secretsmanager; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SecretsmanagerApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SecretsmanagerApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /snowflake/src/main/java/org/example/snowflake/SpringBootSnowflakeApplication.java: -------------------------------------------------------------------------------- 1 | package org.example.snowflake; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootSnowflakeApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootSnowflakeApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /parameterstore/src/main/java/com/example/parameterstore/GreetingController.java: -------------------------------------------------------------------------------- 1 | package com.example.parameterstore; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class GreetingController { 9 | 10 | @Value("${text}") 11 | private String text; 12 | 13 | @GetMapping("/greetings") 14 | public String greeting() { 15 | return this.text; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /secretsmanager/src/main/java/com/example/secretsmanager/GreetingController.java: -------------------------------------------------------------------------------- 1 | package com.example.secretsmanager; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class GreetingController { 9 | 10 | @Value("${text}") 11 | private String secret; 12 | 13 | @GetMapping("/greetings") 14 | public String greeting() { 15 | return this.secret; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/ 8 | *.iws 9 | *.iml 10 | *.ipr 11 | 12 | ### Eclipse ### 13 | .apt_generated 14 | .classpath 15 | .factorypath 16 | .project 17 | .settings 18 | .springBeans 19 | .sts4-cache 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | build/ 28 | !**/src/main/**/build/ 29 | !**/src/test/**/build/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | 34 | ### Mac OS ### 35 | .DS_Store -------------------------------------------------------------------------------- /s3/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} %-5level %logger - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /cloudwatch/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} %-5level %logger - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /mq-broker/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} %-5level %logger - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /snowflake/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} %-5level %logger - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /api-gateway/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} %-5level %logger - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /parameterstore/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} %-5level %logger - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /secretsmanager/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} %-5level %logger - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /step-functions/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /cloudwatch/src/main/java/com/example/cloudwatch/GreetingController.java: -------------------------------------------------------------------------------- 1 | package com.example.cloudwatch; 2 | 3 | import io.micrometer.core.annotation.Timed; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.PathVariable; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @Timed 10 | @RestController 11 | @RequestMapping("/greetings") 12 | public class GreetingController { 13 | 14 | @GetMapping 15 | public String greetings() { 16 | return "Hello World"; 17 | } 18 | 19 | @GetMapping("/{name}") 20 | public String greetings(@PathVariable String name) { 21 | return "Hello " + name; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lambda/src/main/java/com/example/springcloudfunctionaws/SpringCloudFunctionAwsApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.springcloudfunctionaws; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.jdbc.core.JdbcTemplate; 7 | 8 | import java.util.function.Function; 9 | 10 | @SpringBootApplication 11 | public class SpringCloudFunctionAwsApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(SpringCloudFunctionAwsApplication.class, args); 15 | } 16 | 17 | @Bean 18 | public Function fetchByName(JdbcTemplate jdbcTemplate) { 19 | return profile -> jdbcTemplate.queryForObject("SELECT COUNT(*) FROM profile WHERE name LIKE ?", Integer.class, 20 | profile.name() + "%"); 21 | } 22 | 23 | record Profile(String name) { 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.10/apache-maven-3.9.10-bin.zip 20 | -------------------------------------------------------------------------------- /cognito/src/main/java/com/example/cognito/ExampleSecurity.java: -------------------------------------------------------------------------------- 1 | package com.example.cognito; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.http.HttpMethod; 6 | import org.springframework.security.config.Customizer; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 9 | import org.springframework.security.web.SecurityFilterChain; 10 | 11 | @Configuration 12 | @EnableWebSecurity 13 | public class ExampleSecurity { 14 | 15 | @Bean 16 | protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 17 | return http 18 | .authorizeHttpRequests(c -> c.requestMatchers(HttpMethod.GET, "/topsecret") 19 | .authenticated() 20 | .requestMatchers(HttpMethod.GET, "/") 21 | .permitAll()) 22 | .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())) 23 | .build(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /cognito/src/main/java/com/example/cognito/ExampleController.java: -------------------------------------------------------------------------------- 1 | package com.example.cognito; 2 | 3 | import org.springframework.security.core.Authentication; 4 | import org.springframework.security.core.context.SecurityContextHolder; 5 | import org.springframework.security.oauth2.jwt.Jwt; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @RestController 10 | public class ExampleController { 11 | 12 | @GetMapping("/") 13 | public String unsecured() { 14 | return "No secrets here!\n"; 15 | } 16 | 17 | @GetMapping("/topsecret") 18 | String secured() { 19 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 20 | if (authentication != null && authentication.getPrincipal() instanceof Jwt) { 21 | Jwt jwt = (Jwt) authentication.getPrincipal(); 22 | return String.format("You are [%s] with e-mail address [%s].%n", jwt.getSubject(), 23 | jwt.getClaimAsString("email")); 24 | } 25 | else { 26 | return "Something went wrong; authentication is not provided by Cognito.\n"; 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Eddú Meléndez Gonzales 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. 22 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | testcontainers-localstack 8 | 0.0.1-SNAPSHOT 9 | testcontainers-localstack 10 | Demo project for Testcontainers LocalStack Samples 11 | pom 12 | 13 | 14 | api-gateway 15 | cloudwatch 16 | cognito 17 | lambda 18 | mq-broker 19 | parameterstore 20 | s3 21 | secretsmanager 22 | ses 23 | snowflake 24 | sqs 25 | step-functions 26 | 27 | 28 | 29 | 30 | 31 | io.spring.javaformat 32 | spring-javaformat-maven-plugin 33 | 0.0.47 34 | 35 | 36 | validate 37 | true 38 | 39 | validate 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | concurrency: 10 | group: "${{ github.workflow }}-${{ github.head_ref || github.sha }}" 11 | cancel-in-progress: true 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | find_jobs: 18 | runs-on: ubuntu-24.04 19 | outputs: 20 | matrix: ${{ steps.set-matrix.outputs.matrix }} 21 | steps: 22 | - uses: actions/checkout@v6 23 | - uses: actions/setup-java@v5 24 | with: 25 | java-version: '25' 26 | distribution: temurin 27 | cache: maven 28 | - id: set-matrix 29 | run: | 30 | TASKS=$(./mvnw help:evaluate -Dexpression=project.modules -q -DforceStdout | sed -n 's|.*\(.*\).*|"\1"|p' | paste -sd "," - | sed 's/^/[/' | sed 's/$/]/') 31 | echo $TASKS 32 | echo "matrix={\"modules\":$TASKS}" >> $GITHUB_OUTPUT 33 | 34 | build: 35 | name: "${{ matrix.modules }}" 36 | needs: [find_jobs] 37 | runs-on: ubuntu-24.04 38 | strategy: 39 | fail-fast: false 40 | matrix: ${{ fromJson(needs.find_jobs.outputs.matrix) }} 41 | steps: 42 | - name: Free Disk Space 43 | uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 44 | with: 45 | large-packages: false 46 | - uses: actions/checkout@v4 47 | - uses: actions/setup-java@v4 48 | with: 49 | java-version: '25' 50 | distribution: temurin 51 | cache: maven 52 | cache-dependency-path: ${{ matrix.modules }}/pom.xml 53 | - name: Build with Maven (${{ matrix.modules }}) 54 | run: ./mvnw -V -B verify -pl ${{ matrix.modules }} 55 | env: 56 | LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }} 57 | -------------------------------------------------------------------------------- /snowflake/src/test/java/org/example/snowflake/SnowflakeTest.java: -------------------------------------------------------------------------------- 1 | package org.example.snowflake; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.jdbc.core.simple.JdbcClient; 8 | import org.springframework.test.context.DynamicPropertyRegistry; 9 | import org.springframework.test.context.DynamicPropertySource; 10 | import org.testcontainers.containers.GenericContainer; 11 | import org.testcontainers.containers.wait.strategy.Wait; 12 | import org.testcontainers.junit.jupiter.Container; 13 | import org.testcontainers.junit.jupiter.Testcontainers; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | @SpringBootTest(properties = { "spring.datasource.username=test", "spring.datasource.password=test", 18 | "spring.datasource.driver-class-name=net.snowflake.client.jdbc.SnowflakeDriver", }) 19 | @Testcontainers 20 | @EnabledIfEnvironmentVariable(named = "LOCALSTACK_AUTH_TOKEN", matches = ".+") 21 | public class SnowflakeTest { 22 | 23 | @Container 24 | private static final GenericContainer container = new GenericContainer<>("localstack/snowflake:1.5.0") 25 | .withExposedPorts(4566) 26 | .withEnv("LOCALSTACK_AUTH_TOKEN", System.getenv("LOCALSTACK_AUTH_TOKEN")) 27 | .waitingFor(Wait.forLogMessage(".*Ready.*", 1)); 28 | 29 | @DynamicPropertySource 30 | static void setProperties(DynamicPropertyRegistry registry) { 31 | registry.add("spring.datasource.url", () -> "jdbc:snowflake://snowflake.localhost.localstack.cloud:" 32 | + container.getMappedPort(4566) + "/?db=test&schema=test&JDBC_QUERY_RESULT_FORMAT=JSON"); 33 | } 34 | 35 | @Autowired 36 | private JdbcClient jdbcClient; 37 | 38 | @Test 39 | void test() { 40 | String name = this.jdbcClient.sql("select name from profile").query(String.class).single(); 41 | assertThat(name).isEqualTo("test"); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /secretsmanager/src/test/java/com/example/secretsmanager/SecretsmanagerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.secretsmanager; 2 | 3 | import io.restassured.RestAssured; 4 | import org.junit.jupiter.api.BeforeAll; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.boot.test.web.server.LocalServerPort; 9 | import org.springframework.test.context.ContextConfiguration; 10 | import org.springframework.test.context.DynamicPropertyRegistry; 11 | import org.springframework.test.context.DynamicPropertySource; 12 | import org.testcontainers.containers.localstack.LocalStackContainer; 13 | import org.testcontainers.junit.jupiter.Container; 14 | import org.testcontainers.junit.jupiter.Testcontainers; 15 | import org.testcontainers.utility.DockerImageName; 16 | 17 | import java.io.IOException; 18 | 19 | import static org.hamcrest.Matchers.equalTo; 20 | 21 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, 22 | properties = { "spring.cloud.aws.credentials.access-key=noop", "spring.cloud.aws.credentials.secret-key=noop", 23 | "spring.cloud.aws.region.static=us-east-1" }) 24 | @ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class) 25 | @Testcontainers 26 | class SecretsmanagerApplicationTests { 27 | 28 | @Container 29 | private static LocalStackContainer localstack = new LocalStackContainer( 30 | DockerImageName.parse("localstack/localstack:4.12.0")); 31 | 32 | @LocalServerPort 33 | private int localPort; 34 | 35 | private static final String text = "This is not a secret anymore. Love testing AWS with Testcontainers and LocalStack."; 36 | 37 | @DynamicPropertySource 38 | static void properties(DynamicPropertyRegistry registry) { 39 | registry.add("spring.cloud.aws.secretsmanager.endpoint", () -> localstack.getEndpoint().toString()); 40 | registry.add("spring.cloud.aws.secretsmanager.region", localstack::getRegion); 41 | registry.add("spring.config.import", () -> "aws-secretsmanager:/spring/secret/text"); 42 | } 43 | 44 | @BeforeAll 45 | static void beforeAll() throws IOException, InterruptedException { 46 | localstack.execInContainer("awslocal", "secretsmanager", "create-secret", "--name", "/spring/secret/text", 47 | "--secret-string", text, "--region", localstack.getRegion()); 48 | } 49 | 50 | @Test 51 | void contextLoads() { 52 | RestAssured.given().port(this.localPort).get("/greetings").then().assertThat().body(equalTo(text)); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /step-functions/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | step-functions 13 | 0.0.1-SNAPSHOT 14 | step-functions 15 | Demo project for Step Functions with LocalStack 16 | 17 | 25 18 | 1.21.4 19 | 2.29.34 20 | 21 | 22 | 23 | software.amazon.awssdk 24 | sfn 25 | ${aws-java-sdk-v2.version} 26 | test 27 | 28 | 29 | software.amazon.awssdk 30 | iam 31 | ${aws-java-sdk-v2.version} 32 | test 33 | 34 | 35 | software.amazon.awssdk 36 | cloudformation 37 | ${aws-java-sdk-v2.version} 38 | test 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | org.testcontainers 48 | junit-jupiter 49 | test 50 | 51 | 52 | org.testcontainers 53 | localstack 54 | test 55 | 56 | 57 | org.awaitility 58 | awaitility 59 | test 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.testcontainers 67 | testcontainers-bom 68 | ${testcontainers.version} 69 | pom 70 | import 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /parameterstore/src/test/java/com/example/parameterstore/ParameterstoreApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.parameterstore; 2 | 3 | import io.restassured.RestAssured; 4 | import org.junit.jupiter.api.BeforeAll; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 9 | import org.springframework.boot.test.web.server.LocalServerPort; 10 | import org.springframework.test.context.ContextConfiguration; 11 | import org.springframework.test.context.DynamicPropertyRegistry; 12 | import org.springframework.test.context.DynamicPropertySource; 13 | import org.testcontainers.containers.localstack.LocalStackContainer; 14 | import org.testcontainers.junit.jupiter.Container; 15 | import org.testcontainers.junit.jupiter.Testcontainers; 16 | import org.testcontainers.utility.DockerImageName; 17 | 18 | import java.io.IOException; 19 | 20 | import static org.hamcrest.Matchers.equalTo; 21 | 22 | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, 23 | properties = { "spring.cloud.aws.credentials.access-key=noop", "spring.cloud.aws.credentials.secret-key=noop", 24 | "spring.cloud.aws.region.static=us-east-1" }) 25 | @ContextConfiguration(initializers = ConfigDataApplicationContextInitializer.class) 26 | @Testcontainers 27 | class ParameterstoreApplicationTests { 28 | 29 | @LocalServerPort 30 | private int localPort; 31 | 32 | @Container 33 | private static LocalStackContainer localstack = new LocalStackContainer( 34 | DockerImageName.parse("localstack/localstack:4.12.0")); 35 | 36 | @DynamicPropertySource 37 | static void properties(DynamicPropertyRegistry registry) { 38 | registry.add("spring.cloud.aws.parameterstore.endpoint", () -> localstack.getEndpoint().toString()); 39 | registry.add("spring.cloud.aws.parameterstore.region", localstack::getRegion); 40 | registry.add("spring.cloud.aws.endpoint", () -> localstack.getEndpoint().toString()); 41 | registry.add("spring.cloud.aws.region.static", localstack::getRegion); 42 | registry.add("spring.config.import", () -> "aws-parameterstore:/spring/config/"); 43 | } 44 | 45 | @BeforeAll 46 | static void beforeAll() throws IOException, InterruptedException { 47 | localstack.execInContainer("awslocal", "ssm", "put-parameter", "--name", "/spring/config/text", "--value", 48 | "Hello World", "--type", "String", "--region", localstack.getRegion()); 49 | } 50 | 51 | @Test 52 | void contextLoads() { 53 | RestAssured.given().port(this.localPort).get("/greetings").then().assertThat().body(equalTo("Hello World")); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /sqs/src/test/java/com/example/sqs/SqsApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.sqs; 2 | 3 | import io.awspring.cloud.sqs.annotation.SqsListener; 4 | import io.awspring.cloud.sqs.operations.SqsTemplate; 5 | import org.awaitility.Awaitility; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.extension.AfterAllCallback; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.junit.jupiter.api.extension.ExtensionContext; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.boot.test.context.TestConfiguration; 13 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 14 | import org.springframework.context.ConfigurableApplicationContext; 15 | import org.springframework.context.annotation.Bean; 16 | import org.springframework.test.context.junit.jupiter.SpringExtension; 17 | import org.testcontainers.containers.localstack.LocalStackContainer; 18 | import org.testcontainers.junit.jupiter.Container; 19 | import org.testcontainers.junit.jupiter.Testcontainers; 20 | import org.testcontainers.utility.DockerImageName; 21 | 22 | import java.time.Duration; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | 28 | @SpringBootTest 29 | @Testcontainers 30 | @ExtendWith(SqsApplicationTests.SqsAfterAllCallBack.class) 31 | class SqsApplicationTests { 32 | 33 | @Container 34 | @ServiceConnection 35 | static LocalStackContainer localStackContainer = new LocalStackContainer( 36 | DockerImageName.parse("localstack/localstack:4.12.0")); 37 | 38 | @Autowired 39 | private SqsTemplate sqsTemplate; 40 | 41 | @Autowired 42 | private TestListener testListener; 43 | 44 | @Test 45 | void consumeMessage() { 46 | this.sqsTemplate.send("test", "Hello World!"); 47 | 48 | Awaitility.waitAtMost(Duration.ofSeconds(30)).untilAsserted(() -> { 49 | assertThat(this.testListener.messages).hasSize(1); 50 | }); 51 | } 52 | 53 | @TestConfiguration(proxyBeanMethods = false) 54 | static class TestConfig { 55 | 56 | @Bean 57 | TestListener testListener() { 58 | return new TestListener(); 59 | } 60 | 61 | } 62 | 63 | static class TestListener { 64 | 65 | private final List messages = new ArrayList<>(); 66 | 67 | @SqsListener("test") 68 | void listen(String message) { 69 | this.messages.add(message); 70 | } 71 | 72 | } 73 | 74 | static class SqsAfterAllCallBack implements AfterAllCallback { 75 | 76 | @Override 77 | public void afterAll(ExtensionContext context) throws Exception { 78 | ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) SpringExtension 79 | .getApplicationContext(context); 80 | applicationContext.stop(); 81 | } 82 | 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /api-gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | api-gateway 13 | 0.0.1-SNAPSHOT 14 | api-gateway 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 2.26.20 19 | 1.21.4 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-test 30 | test 31 | 32 | 33 | org.testcontainers 34 | junit-jupiter 35 | test 36 | 37 | 38 | org.testcontainers 39 | localstack 40 | test 41 | 42 | 43 | io.rest-assured 44 | rest-assured 45 | test 46 | 47 | 48 | software.amazon.awssdk 49 | apigateway 50 | test 51 | 52 | 53 | 54 | 55 | 56 | 57 | software.amazon.awssdk 58 | bom 59 | ${awssdk-v2.version} 60 | pom 61 | import 62 | 63 | 64 | 65 | 66 | 67 | 68 | spring-milestones 69 | Spring Milestones 70 | https://repo.spring.io/milestone 71 | 72 | false 73 | 74 | 75 | 76 | 77 | 78 | spring-milestones 79 | Spring Milestones 80 | https://repo.spring.io/milestone 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /snowflake/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | snowflake 13 | 0.0.1-SNAPSHOT 14 | snowflake 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 1.21.4 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-jdbc 24 | 25 | 26 | org.flywaydb 27 | flyway-core 28 | 29 | 30 | org.flywaydb 31 | flyway-database-snowflake 32 | 33 | 34 | 35 | net.snowflake 36 | snowflake-jdbc 37 | 3.26.1 38 | runtime 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | org.testcontainers 48 | junit-jupiter 49 | test 50 | 51 | 52 | org.assertj 53 | assertj-core 54 | test 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-maven-plugin 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-surefire-plugin 67 | 68 | --add-opens=java.base/java.nio=ALL-UNNAMED 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | spring-milestones 77 | Spring Milestones 78 | https://repo.spring.io/milestone 79 | 80 | false 81 | 82 | 83 | 84 | 85 | 86 | spring-milestones 87 | Spring Milestones 88 | https://repo.spring.io/milestone 89 | 90 | false 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /ses/src/test/java/com/example/ses/SesApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.ses; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import org.junit.jupiter.api.BeforeAll; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 9 | import org.springframework.core.ParameterizedTypeReference; 10 | import org.springframework.mail.MailSender; 11 | import org.springframework.mail.SimpleMailMessage; 12 | import org.springframework.web.client.RestClient; 13 | import org.testcontainers.containers.localstack.LocalStackContainer; 14 | import org.testcontainers.junit.jupiter.Container; 15 | import org.testcontainers.junit.jupiter.Testcontainers; 16 | import org.testcontainers.utility.DockerImageName; 17 | 18 | import java.io.IOException; 19 | import java.util.List; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | 23 | @SpringBootTest 24 | @Testcontainers 25 | class SesApplicationTests { 26 | 27 | @Container 28 | @ServiceConnection 29 | static LocalStackContainer localStackContainer = new LocalStackContainer( 30 | DockerImageName.parse("localstack/localstack:4.12.0")); 31 | 32 | @Autowired 33 | private MailSender mailSender; 34 | 35 | @BeforeAll 36 | static void beforeAll() throws IOException, InterruptedException { 37 | localStackContainer.execInContainer("awslocal", "ses", "verify-email-identity", "--email", "hello@example.com"); 38 | } 39 | 40 | @Test 41 | void consumeMessage() { 42 | SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); 43 | simpleMailMessage.setFrom("hello@example.com"); 44 | simpleMailMessage.setTo("bar@baz.com"); 45 | simpleMailMessage.setSubject("test subject"); 46 | simpleMailMessage.setText("test content"); 47 | this.mailSender.send(simpleMailMessage); 48 | var restClient = RestClient.builder().baseUrl(localStackContainer.getEndpoint().toString()).build(); 49 | var response = restClient.get() 50 | .uri("/_aws/ses", uriBuilder -> uriBuilder.queryParam("email", "hello@example.com").build()) 51 | .retrieve() 52 | .body(new ParameterizedTypeReference() { 53 | }); 54 | assertThat(response.messages()).hasSize(1); 55 | Message message = response.messages().getFirst(); 56 | assertThat(message.source()).isEqualTo("hello@example.com"); 57 | assertThat(message.subject()).isEqualTo("test subject"); 58 | assertThat(message.body().textPart()).isEqualTo("test content"); 59 | } 60 | 61 | record Messages(List messages) { 62 | 63 | } 64 | 65 | record Message(@JsonProperty("Id") String id, @JsonProperty("Region") String region, 66 | @JsonProperty("Destination") Destination destination, @JsonProperty("Source") String source, 67 | @JsonProperty("Subject") String subject, @JsonProperty("Body") Body body) { 68 | 69 | } 70 | 71 | record Destination(@JsonProperty("ToAddresses") List toAddresses) { 72 | 73 | } 74 | 75 | record Body(@JsonProperty("text_part") String textPart, @JsonProperty("html_part") String htmlPart) { 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /ses/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | ses 13 | 0.0.1-SNAPSHOT 14 | ses 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 1.21.4 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | io.awspring.cloud 27 | spring-cloud-aws-starter-ses 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | org.testcontainers 37 | junit-jupiter 38 | test 39 | 40 | 41 | org.testcontainers 42 | localstack 43 | test 44 | 45 | 46 | org.awaitility 47 | awaitility 48 | test 49 | 50 | 51 | io.awspring.cloud 52 | spring-cloud-aws-testcontainers 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | io.awspring.cloud 70 | spring-cloud-aws-dependencies 71 | 3.4.0 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | spring-milestones 81 | Spring Milestones 82 | https://repo.spring.io/milestone 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | spring-milestones 91 | Spring Milestones 92 | https://repo.spring.io/milestone 93 | 94 | false 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /sqs/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | sqs 13 | 0.0.1-SNAPSHOT 14 | sqs 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 1.21.4 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | io.awspring.cloud 27 | spring-cloud-aws-starter-sqs 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | org.testcontainers 37 | junit-jupiter 38 | test 39 | 40 | 41 | org.testcontainers 42 | localstack 43 | test 44 | 45 | 46 | org.awaitility 47 | awaitility 48 | test 49 | 50 | 51 | io.awspring.cloud 52 | spring-cloud-aws-testcontainers 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | io.awspring.cloud 70 | spring-cloud-aws-dependencies 71 | 3.4.0 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | spring-milestones 81 | Spring Milestones 82 | https://repo.spring.io/milestone 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | spring-milestones 91 | Spring Milestones 92 | https://repo.spring.io/milestone 93 | 94 | false 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /parameterstore/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | parameterstore 13 | 0.0.1-SNAPSHOT 14 | parameterstore 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 1.21.4 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | io.awspring.cloud 27 | spring-cloud-aws-starter-parameter-store 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | org.testcontainers 37 | junit-jupiter 38 | test 39 | 40 | 41 | org.testcontainers 42 | localstack 43 | test 44 | 45 | 46 | io.rest-assured 47 | rest-assured 48 | test 49 | 50 | 51 | org.awaitility 52 | awaitility 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | io.awspring.cloud 70 | spring-cloud-aws-dependencies 71 | 3.4.0 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | spring-milestones 81 | Spring Milestones 82 | https://repo.spring.io/milestone 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | spring-milestones 91 | Spring Milestones 92 | https://repo.spring.io/milestone 93 | 94 | false 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /secretsmanager/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | secretsmanager 13 | 0.0.1-SNAPSHOT 14 | secretsmanager 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 1.21.4 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | io.awspring.cloud 27 | spring-cloud-aws-starter-secrets-manager 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | org.testcontainers 37 | junit-jupiter 38 | test 39 | 40 | 41 | org.testcontainers 42 | localstack 43 | test 44 | 45 | 46 | io.rest-assured 47 | rest-assured 48 | test 49 | 50 | 51 | org.awaitility 52 | awaitility 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-maven-plugin 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | io.awspring.cloud 70 | spring-cloud-aws-dependencies 71 | 3.4.0 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | spring-milestones 81 | Spring Milestones 82 | https://repo.spring.io/milestone 83 | 84 | false 85 | 86 | 87 | 88 | 89 | 90 | spring-milestones 91 | Spring Milestones 92 | https://repo.spring.io/milestone 93 | 94 | false 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /cloudwatch/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | cloudwatch 13 | 0.0.1-SNAPSHOT 14 | cloudwatch 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 1.21.4 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | io.awspring.cloud 27 | spring-cloud-aws-starter-metrics 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | org.testcontainers 37 | junit-jupiter 38 | test 39 | 40 | 41 | org.testcontainers 42 | localstack 43 | test 44 | 45 | 46 | io.rest-assured 47 | rest-assured 48 | test 49 | 50 | 51 | org.awaitility 52 | awaitility 53 | test 54 | 55 | 56 | io.awspring.cloud 57 | spring-cloud-aws-testcontainers 58 | test 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-maven-plugin 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | io.awspring.cloud 75 | spring-cloud-aws-dependencies 76 | 3.4.0 77 | pom 78 | import 79 | 80 | 81 | 82 | 83 | 84 | 85 | spring-milestones 86 | Spring Milestones 87 | https://repo.spring.io/milestone 88 | 89 | false 90 | 91 | 92 | 93 | 94 | 95 | spring-milestones 96 | Spring Milestones 97 | https://repo.spring.io/milestone 98 | 99 | false 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /cognito/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | cognito 13 | 0.0.1-SNAPSHOT 14 | cognito 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 2.26.20 19 | 1.21.4 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-oauth2-client 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-security 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-oauth2-resource-server 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-web 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-test 42 | test 43 | 44 | 45 | org.testcontainers 46 | junit-jupiter 47 | test 48 | 49 | 50 | org.testcontainers 51 | localstack 52 | test 53 | 54 | 55 | software.amazon.awssdk 56 | cognitoidentityprovider 57 | test 58 | 59 | 60 | 61 | 62 | 63 | 64 | software.amazon.awssdk 65 | bom 66 | ${awssdk-v2.version} 67 | pom 68 | import 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | org.springframework.boot 77 | spring-boot-maven-plugin 78 | 79 | 80 | 81 | 82 | 83 | 84 | spring-milestones 85 | Spring Milestones 86 | https://repo.spring.io/milestone 87 | 88 | false 89 | 90 | 91 | 92 | 93 | 94 | spring-milestones 95 | Spring Milestones 96 | https://repo.spring.io/milestone 97 | 98 | false 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /mq-broker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | mq-broker 13 | 0.0.1-SNAPSHOT 14 | mq-broker 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 2.26.20 19 | 1.21.4 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-test 25 | test 26 | 27 | 28 | org.testcontainers 29 | junit-jupiter 30 | test 31 | 32 | 33 | org.testcontainers 34 | localstack 35 | test 36 | 37 | 38 | software.amazon.awssdk 39 | mq 40 | test 41 | 42 | 43 | org.junit.jupiter 44 | junit-jupiter 45 | test 46 | 47 | 48 | org.assertj 49 | assertj-core 50 | test 51 | 52 | 53 | org.awaitility 54 | awaitility 55 | test 56 | 57 | 58 | io.rest-assured 59 | rest-assured 60 | test 61 | 62 | 63 | org.apache.activemq 64 | activemq-client 65 | test 66 | 67 | 68 | jakarta.jms 69 | jakarta.jms-api 70 | test 71 | 72 | 73 | 74 | 75 | 76 | 77 | software.amazon.awssdk 78 | bom 79 | ${awssdk-v2.version} 80 | pom 81 | import 82 | 83 | 84 | 85 | 86 | 87 | 88 | spring-milestones 89 | Spring Milestones 90 | https://repo.spring.io/milestone 91 | 92 | false 93 | 94 | 95 | 96 | 97 | 98 | spring-milestones 99 | Spring Milestones 100 | https://repo.spring.io/milestone 101 | 102 | false 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /s3/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | s3 13 | 0.0.1-SNAPSHOT 14 | s3 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 1.21.4 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | io.awspring.cloud 27 | spring-cloud-aws-starter-s3 28 | 29 | 30 | io.awspring.cloud 31 | spring-cloud-aws-starter-sqs 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-test 37 | test 38 | 39 | 40 | org.testcontainers 41 | junit-jupiter 42 | test 43 | 44 | 45 | org.testcontainers 46 | localstack 47 | test 48 | 49 | 50 | io.rest-assured 51 | rest-assured 52 | test 53 | 54 | 55 | org.awaitility 56 | awaitility 57 | test 58 | 59 | 60 | io.awspring.cloud 61 | spring-cloud-aws-testcontainers 62 | test 63 | 64 | 65 | software.amazon.awssdk 66 | s3-event-notifications 67 | test 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | io.awspring.cloud 84 | spring-cloud-aws-dependencies 85 | 3.4.0 86 | pom 87 | import 88 | 89 | 90 | 91 | 92 | 93 | 94 | spring-milestones 95 | Spring Milestones 96 | https://repo.spring.io/milestone 97 | 98 | false 99 | 100 | 101 | 102 | 103 | 104 | spring-milestones 105 | Spring Milestones 106 | https://repo.spring.io/milestone 107 | 108 | false 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /cloudwatch/src/test/java/com/example/cloudwatch/CloudwatchApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.cloudwatch; 2 | 3 | import io.restassured.RestAssured; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 9 | import org.springframework.boot.test.web.server.LocalServerPort; 10 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 11 | import org.testcontainers.containers.localstack.LocalStackContainer; 12 | import org.testcontainers.junit.jupiter.Container; 13 | import org.testcontainers.junit.jupiter.Testcontainers; 14 | import org.testcontainers.utility.DockerImageName; 15 | import software.amazon.awssdk.services.cloudwatch.CloudWatchAsyncClient; 16 | import software.amazon.awssdk.services.cloudwatch.model.*; 17 | 18 | import java.time.Duration; 19 | import java.time.Instant; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.awaitility.Awaitility.await; 23 | import static org.hamcrest.Matchers.equalTo; 24 | 25 | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, 26 | properties = { "management.cloudwatch.metrics.export.namespace=tc-localstack", 27 | "management.cloudwatch.metrics.export.step=5s", "management.metrics.enable.all=false", 28 | "management.metrics.enable.http=true" }) 29 | @AutoConfigureObservability(tracing = false) 30 | @Testcontainers 31 | class CloudwatchApplicationTests { 32 | 33 | @Container 34 | @ServiceConnection 35 | private static LocalStackContainer localstack = new LocalStackContainer( 36 | DockerImageName.parse("localstack/localstack:4.12.0")); 37 | 38 | @Autowired 39 | private CloudWatchAsyncClient cloudWatchAsyncClient; 40 | 41 | @LocalServerPort 42 | private int localPort; 43 | 44 | @Test 45 | void contextLoads() { 46 | Instant startTime = Instant.now(); 47 | Instant endTime = startTime.plus(Duration.ofSeconds(30)); 48 | 49 | for (int i = 0; i < 5; i++) { 50 | RestAssured.given().port(this.localPort).get("/greetings").then().assertThat().body(equalTo("Hello World")); 51 | } 52 | 53 | Dimension error = Dimension.builder().name("error").value("none").build(); 54 | Dimension exception = Dimension.builder().name("exception").value("none").build(); 55 | Dimension method = Dimension.builder().name("method").value("GET").build(); 56 | Dimension outcome = Dimension.builder().name("outcome").value("SUCCESS").build(); 57 | Dimension uri = Dimension.builder().name("uri").value("/greetings").build(); 58 | Dimension status = Dimension.builder().name("status").value("200").build(); 59 | Metric metric = Metric.builder() 60 | .namespace("tc-localstack") 61 | .metricName("http.server.requests.count") 62 | .dimensions(error, exception, method, outcome, uri, status) 63 | .build(); 64 | MetricStat metricStat = MetricStat.builder() 65 | .stat("Maximum") 66 | .metric(metric) 67 | .unit(StandardUnit.COUNT) 68 | .period(5) 69 | .build(); 70 | MetricDataQuery metricDataQuery = MetricDataQuery.builder() 71 | .metricStat(metricStat) 72 | .id("test1") 73 | .returnData(true) 74 | .build(); 75 | await().atMost(Duration.ofSeconds(20)) 76 | .pollInterval(Duration.ofSeconds(5)) 77 | .ignoreExceptions() 78 | .untilAsserted(() -> { 79 | GetMetricDataResponse response = this.cloudWatchAsyncClient 80 | .getMetricData(GetMetricDataRequest.builder() 81 | .startTime(startTime) 82 | .endTime(endTime) 83 | .metricDataQueries(metricDataQuery) 84 | .build()) 85 | .get(); 86 | assertThat(response.metricDataResults()).hasSize(1); 87 | assertThat(response.metricDataResults().get(0).label()).isEqualTo("http.server.requests.count"); 88 | assertThat(response.metricDataResults().get(0).values()).contains(5d); 89 | }); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /mq-broker/src/test/java/com/example/mqbroker/MqBrokerTests.java: -------------------------------------------------------------------------------- 1 | package com.example.mqbroker; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; 5 | import org.testcontainers.containers.localstack.LocalStackContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | import org.testcontainers.junit.jupiter.Testcontainers; 8 | import org.testcontainers.utility.DockerImageName; 9 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 10 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; 11 | import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; 12 | import software.amazon.awssdk.regions.Region; 13 | import software.amazon.awssdk.services.mq.MqClient; 14 | import software.amazon.awssdk.services.mq.model.BrokerState; 15 | import software.amazon.awssdk.services.mq.model.CreateBrokerRequest; 16 | import software.amazon.awssdk.services.mq.model.CreateBrokerResponse; 17 | import software.amazon.awssdk.services.mq.model.DescribeBrokerRequest; 18 | import software.amazon.awssdk.services.mq.model.EngineType; 19 | import software.amazon.awssdk.services.mq.model.User; 20 | 21 | import jakarta.jms.Connection; 22 | import jakarta.jms.ConnectionFactory; 23 | import jakarta.jms.Message; 24 | import jakarta.jms.MessageConsumer; 25 | import jakarta.jms.Queue; 26 | import jakarta.jms.Session; 27 | import jakarta.jms.TextMessage; 28 | import java.time.Duration; 29 | 30 | import org.apache.activemq.ActiveMQConnectionFactory; 31 | 32 | import static io.restassured.RestAssured.given; 33 | import static org.assertj.core.api.Assertions.assertThat; 34 | import static org.awaitility.Awaitility.await; 35 | 36 | @Testcontainers 37 | @EnabledIfEnvironmentVariable(named = "LOCALSTACK_AUTH_TOKEN", matches = ".+") 38 | public class MqBrokerTests { 39 | 40 | @Container 41 | private static final LocalStackContainer localstack = new LocalStackContainer( 42 | DockerImageName.parse("localstack/localstack-pro:4.12.0")) 43 | .withExposedPorts(4566, 4510, 4511) 44 | .withEnv("LOCALSTACK_AUTH_TOKEN", System.getenv("LOCALSTACK_AUTH_TOKEN")); 45 | 46 | @Test 47 | void test() throws Exception { 48 | AwsBasicCredentials awsCreds = AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()); 49 | 50 | try (MqClient mqClient = MqClient.builder() 51 | .region(Region.of(localstack.getRegion())) 52 | .endpointOverride(localstack.getEndpoint()) 53 | .credentialsProvider(StaticCredentialsProvider.create(awsCreds)) 54 | .build()) { 55 | 56 | User adminUser = User.builder().username("admin").password("admin123456789").build(); 57 | 58 | CreateBrokerRequest createBrokerRequest = CreateBrokerRequest.builder() 59 | .brokerName("test-broker") 60 | .engineType(EngineType.ACTIVEMQ) 61 | .engineVersion("5.18") 62 | .hostInstanceType("mq.t3.micro") 63 | .publiclyAccessible(true) 64 | .users(adminUser) 65 | .overrideConfiguration( 66 | AwsRequestOverrideConfiguration.builder().apiCallAttemptTimeout(Duration.ofMinutes(3)).build()) 67 | .build(); 68 | 69 | CreateBrokerResponse createBrokerResponse = mqClient.createBroker(createBrokerRequest); 70 | String brokerId = createBrokerResponse.brokerId(); 71 | 72 | DescribeBrokerRequest describeBrokerRequest = DescribeBrokerRequest.builder().brokerId(brokerId).build(); 73 | 74 | await().atMost(Duration.ofMinutes(2)).pollInterval(Duration.ofSeconds(5)).untilAsserted(() -> { 75 | BrokerState brokerState = mqClient.describeBroker(describeBrokerRequest).brokerState(); 76 | System.out.println("Broker state: " + brokerState); 77 | assertThat(brokerState).isEqualTo(BrokerState.RUNNING); 78 | }); 79 | 80 | String messageContent = "Hello, ActiveMQ!"; 81 | given().baseUri("http://%s:%d".formatted(localstack.getHost(), localstack.getMappedPort(4510))) 82 | .auth() 83 | .preemptive() 84 | .basic("admin", "admin") 85 | .queryParam("destination", "queue://test.topic") 86 | .body(messageContent) 87 | .post("/api/message") 88 | .then() 89 | .statusCode(200); 90 | 91 | String brokerEndpoint = "tcp://%s:%d".formatted(localstack.getHost(), localstack.getMappedPort(4511)); 92 | 93 | ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("admin", "admin123456789", 94 | brokerEndpoint); 95 | 96 | try (Connection connection = connectionFactory.createConnection()) { 97 | connection.start(); 98 | Session session = connection.createSession(); 99 | Queue queue = session.createQueue("test.topic"); 100 | MessageConsumer consumer = session.createConsumer(queue); 101 | 102 | Message message = consumer.receive(5000); 103 | String receivedContent = ((TextMessage) message).getText(); 104 | assertThat(receivedContent).isEqualToIgnoringNewLines(messageContent); 105 | 106 | consumer.close(); 107 | session.close(); 108 | } 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /api-gateway/src/test/java/com/example/apigateway/ApiGatewayTests.java: -------------------------------------------------------------------------------- 1 | package com.example.apigateway; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.testcontainers.containers.localstack.LocalStackContainer; 5 | import org.testcontainers.junit.jupiter.Container; 6 | import org.testcontainers.junit.jupiter.Testcontainers; 7 | import org.testcontainers.utility.DockerImageName; 8 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 9 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; 10 | import software.amazon.awssdk.regions.Region; 11 | import software.amazon.awssdk.services.apigateway.ApiGatewayClient; 12 | import software.amazon.awssdk.services.apigateway.model.CreateDeploymentRequest; 13 | import software.amazon.awssdk.services.apigateway.model.CreateResourceRequest; 14 | import software.amazon.awssdk.services.apigateway.model.CreateResourceResponse; 15 | import software.amazon.awssdk.services.apigateway.model.CreateRestApiRequest; 16 | import software.amazon.awssdk.services.apigateway.model.CreateRestApiResponse; 17 | import software.amazon.awssdk.services.apigateway.model.GetResourcesRequest; 18 | import software.amazon.awssdk.services.apigateway.model.GetResourcesResponse; 19 | import software.amazon.awssdk.services.apigateway.model.IntegrationType; 20 | import software.amazon.awssdk.services.apigateway.model.PutIntegrationRequest; 21 | import software.amazon.awssdk.services.apigateway.model.PutIntegrationResponseRequest; 22 | import software.amazon.awssdk.services.apigateway.model.PutMethodRequest; 23 | 24 | import java.util.Collections; 25 | 26 | import static io.restassured.RestAssured.given; 27 | import static org.assertj.core.api.Assertions.assertThat; 28 | 29 | @Testcontainers 30 | class ApiGatewayTests { 31 | 32 | @Container 33 | private static final LocalStackContainer localstack = new LocalStackContainer( 34 | DockerImageName.parse("localstack/localstack:4.12.0")); 35 | 36 | @Test 37 | void test() { 38 | AwsBasicCredentials awsCreds = AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()); 39 | try (ApiGatewayClient apiGateway = ApiGatewayClient.builder() 40 | .region(Region.of(localstack.getRegion())) 41 | .endpointOverride(localstack.getEndpoint()) 42 | .credentialsProvider(StaticCredentialsProvider.create(awsCreds)) 43 | .build()) { 44 | 45 | CreateRestApiRequest createApiRequest = CreateRestApiRequest.builder().name("MyAPI").build(); 46 | CreateRestApiResponse createApiResponse = apiGateway.createRestApi(createApiRequest); 47 | String restApiId = createApiResponse.id(); 48 | 49 | GetResourcesRequest getResourcesRequest = GetResourcesRequest.builder().restApiId(restApiId).build(); 50 | GetResourcesResponse getResourcesResponse = apiGateway.getResources(getResourcesRequest); 51 | String rootResourceId = getResourcesResponse.items().get(0).id(); 52 | 53 | CreateResourceRequest createResourceRequest = CreateResourceRequest.builder() 54 | .restApiId(restApiId) 55 | .parentId(rootResourceId) 56 | .pathPart("example") 57 | .build(); 58 | CreateResourceResponse createResourceResponse = apiGateway.createResource(createResourceRequest); 59 | String resourceId = createResourceResponse.id(); 60 | 61 | PutMethodRequest putMethodRequest = PutMethodRequest.builder() 62 | .restApiId(restApiId) 63 | .resourceId(resourceId) 64 | .httpMethod("GET") 65 | .authorizationType("NONE") 66 | .build(); 67 | apiGateway.putMethod(putMethodRequest); 68 | 69 | String requestTemplate = "{ \"statusCode\": 200 }"; 70 | PutIntegrationRequest putIntegrationRequest = PutIntegrationRequest.builder() 71 | .restApiId(restApiId) 72 | .resourceId(resourceId) 73 | .httpMethod("GET") 74 | .type(IntegrationType.MOCK) 75 | .requestTemplates(Collections.singletonMap("application/json", requestTemplate)) 76 | .build(); 77 | apiGateway.putIntegration(putIntegrationRequest); 78 | 79 | String responseTemplate = "{\"message\": \"Hello, $input.params('name')\"}"; 80 | PutIntegrationResponseRequest putIntegrationResponseRequest = PutIntegrationResponseRequest.builder() 81 | .restApiId(restApiId) 82 | .resourceId(resourceId) 83 | .httpMethod("GET") 84 | .statusCode("200") 85 | .responseTemplates(Collections.singletonMap("application/json", responseTemplate)) 86 | .build(); 87 | apiGateway.putIntegrationResponse(putIntegrationResponseRequest); 88 | 89 | CreateDeploymentRequest request = CreateDeploymentRequest.builder() 90 | .restApiId(restApiId) 91 | .stageName("test") 92 | .build(); 93 | apiGateway.createDeployment(request); 94 | 95 | String body = given() 96 | .baseUri("http://%s.execute-api.localhost.localstack.cloud:%d".formatted(restApiId, 97 | localstack.getMappedPort(4566))) 98 | .queryParam("name", "World") 99 | .get("/test/example") 100 | .body() 101 | .asString(); 102 | assertThat(body).contains("Hello, World"); 103 | } 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /s3/src/test/java/com/example/s3/S3ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.s3; 2 | 3 | import io.awspring.cloud.s3.S3Template; 4 | import io.awspring.cloud.sqs.annotation.SqsListener; 5 | import org.junit.jupiter.api.BeforeAll; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.boot.test.context.TestConfiguration; 10 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 11 | import org.springframework.context.annotation.Bean; 12 | import org.testcontainers.containers.localstack.LocalStackContainer; 13 | import org.testcontainers.junit.jupiter.Container; 14 | import org.testcontainers.junit.jupiter.Testcontainers; 15 | import org.testcontainers.utility.DockerImageName; 16 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 17 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; 18 | import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification; 19 | import software.amazon.awssdk.regions.Region; 20 | import software.amazon.awssdk.services.s3.S3Client; 21 | import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; 22 | import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; 23 | import software.amazon.awssdk.services.sqs.SqsClient; 24 | 25 | import java.io.IOException; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | import static org.assertj.core.api.Assertions.assertThat; 30 | 31 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 32 | @Testcontainers 33 | class S3ApplicationTests { 34 | 35 | @Container 36 | @ServiceConnection 37 | private static LocalStackContainer localstack = new LocalStackContainer( 38 | DockerImageName.parse("localstack/localstack:4.12.0")); 39 | 40 | @Autowired 41 | private S3Template s3Template; 42 | 43 | @Autowired 44 | private S3Client s3Client; 45 | 46 | @Autowired 47 | private TestListener testListener; 48 | 49 | private static final String POLICY = """ 50 | { 51 | "QueueConfigurations": [ 52 | { 53 | "QueueArn": "%s", 54 | "Events": ["s3:ObjectCreated:*"] 55 | } 56 | ] 57 | } 58 | """; 59 | 60 | @BeforeAll 61 | static void beforeAll() throws IOException, InterruptedException { 62 | localstack.execInContainer("awslocal", "s3api", "create-bucket", "--bucket", "conferences", "--region", 63 | localstack.getRegion()); 64 | try (var sqsClient = SqsClient.builder() 65 | .region(Region.of(localstack.getRegion())) 66 | .credentialsProvider(StaticCredentialsProvider 67 | .create(AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()))) 68 | .endpointOverride(localstack.getEndpoint()) 69 | .build()) { 70 | var queue = sqsClient.createQueue(builder -> builder.queueName("s3-event-notification-queue")); 71 | var queueAttributes = sqsClient.getQueueAttributes( 72 | builder -> builder.queueUrl(queue.queueUrl()).attributeNamesWithStrings("QueueArn")); 73 | var queueArn = queueAttributes.attributesAsStrings().get("QueueArn"); 74 | localstack.execInContainer("awslocal", "s3api", "put-bucket-notification-configuration", "--bucket", 75 | "conferences", "--notification-configuration", String.format(POLICY, queueArn)); 76 | } 77 | } 78 | 79 | @Test 80 | void contextLoads() { 81 | assertThat(this.s3Client.listBuckets().buckets()).hasSize(1); 82 | 83 | this.s3Template.store("conferences", "javaone.txt", "Las Vegas"); 84 | ListObjectsV2Response listConferencesObjectsV2Response = this.s3Client 85 | .listObjectsV2(ListObjectsV2Request.builder().bucket("conferences").build()); 86 | assertThat(listConferencesObjectsV2Response.contents()).hasSize(1); 87 | 88 | this.s3Template.createBucket("talks"); 89 | assertThat(this.s3Client.listBuckets().buckets()).hasSize(2); 90 | 91 | this.s3Template.store("talks", "Adopting Testcontainers for local development.txt", "Oleg Šelajev"); 92 | ListObjectsV2Response listTalksObjectsV2Response = this.s3Client 93 | .listObjectsV2(ListObjectsV2Request.builder().bucket("talks").build()); 94 | assertThat(listTalksObjectsV2Response.contents()).hasSize(1); 95 | assertThat(testListener.messages).hasSize(2); 96 | var s3EventNotification = S3EventNotification.fromJson(testListener.messages.get(1)); 97 | assertThat(s3EventNotification.getRecords().getFirst().getEventSource()).isEqualTo("aws:s3"); 98 | assertThat(s3EventNotification.getRecords().getFirst().getEventName()).isEqualTo("ObjectCreated:Put"); 99 | } 100 | 101 | @TestConfiguration(proxyBeanMethods = false) 102 | static class TestConfig { 103 | 104 | @Bean 105 | TestListener testListener() { 106 | return new TestListener(); 107 | } 108 | 109 | } 110 | 111 | static class TestListener { 112 | 113 | private final List messages = new ArrayList<>(); 114 | 115 | @SqsListener("s3-event-notification-queue") 116 | void listen(String message) { 117 | this.messages.add(message); 118 | } 119 | 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /step-functions/src/test/java/com/example/stepfunctions/StepFunctionsTests.java: -------------------------------------------------------------------------------- 1 | package com.example.stepfunctions; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.awaitility.Awaitility.await; 5 | 6 | import java.time.Duration; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import org.junit.jupiter.api.Test; 11 | import org.testcontainers.containers.localstack.LocalStackContainer; 12 | import org.testcontainers.junit.jupiter.Container; 13 | import org.testcontainers.junit.jupiter.Testcontainers; 14 | import org.testcontainers.utility.DockerImageName; 15 | 16 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 17 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; 18 | import software.amazon.awssdk.regions.Region; 19 | import software.amazon.awssdk.services.iam.IamClient; 20 | import software.amazon.awssdk.services.iam.model.CreateRoleRequest; 21 | import software.amazon.awssdk.services.iam.model.CreateRoleResponse; 22 | import software.amazon.awssdk.services.sfn.SfnClient; 23 | import software.amazon.awssdk.services.sfn.model.CreateStateMachineRequest; 24 | import software.amazon.awssdk.services.sfn.model.CreateStateMachineResponse; 25 | import software.amazon.awssdk.services.sfn.model.DescribeExecutionRequest; 26 | import software.amazon.awssdk.services.sfn.model.DescribeExecutionResponse; 27 | import software.amazon.awssdk.services.sfn.model.ExecutionStatus; 28 | import software.amazon.awssdk.services.sfn.model.StartExecutionRequest; 29 | import software.amazon.awssdk.services.sfn.model.StartExecutionResponse; 30 | import software.amazon.awssdk.services.sfn.model.StateMachineType; 31 | 32 | @Testcontainers 33 | class StepFunctionsTests { 34 | 35 | @Container 36 | static LocalStackContainer localstack = new LocalStackContainer( 37 | DockerImageName.parse("localstack/localstack:4.7.0")); 38 | 39 | @Test 40 | void createAndExecuteStateMachine() throws Exception { 41 | try (SfnClient sfnClient = SfnClient.builder() 42 | .endpointOverride(localstack.getEndpoint()) 43 | .credentialsProvider(StaticCredentialsProvider 44 | .create(AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()))) 45 | .region(Region.of(localstack.getRegion())) 46 | .build()) { 47 | 48 | String roleArn = createIamRole(); 49 | 50 | String stateMachineDefinition = """ 51 | { 52 | "Comment": "A Hello World example", 53 | "StartAt": "HelloWorld", 54 | "States": { 55 | "HelloWorld": { 56 | "Type": "Pass", 57 | "Result": "Hello World!", 58 | "End": true 59 | } 60 | } 61 | } 62 | """; 63 | 64 | CreateStateMachineRequest createRequest = CreateStateMachineRequest.builder() 65 | .name("HelloWorldStateMachine") 66 | .definition(stateMachineDefinition) 67 | .roleArn(roleArn) 68 | .type(StateMachineType.STANDARD) 69 | .build(); 70 | 71 | CreateStateMachineResponse createResponse = sfnClient.createStateMachine(createRequest); 72 | assertThat(createResponse.stateMachineArn()).isNotNull(); 73 | 74 | Map input = new HashMap<>(); 75 | input.put("name", "World"); 76 | 77 | StartExecutionRequest startRequest = StartExecutionRequest.builder() 78 | .stateMachineArn(createResponse.stateMachineArn()) 79 | .name("test-execution") 80 | .input("{\"name\": \"World\"}") 81 | .build(); 82 | 83 | StartExecutionResponse startResponse = sfnClient.startExecution(startRequest); 84 | assertThat(startResponse.executionArn()).isNotNull(); 85 | 86 | await().atMost(Duration.ofSeconds(30)).pollInterval(Duration.ofSeconds(1)).until(() -> { 87 | DescribeExecutionResponse execution = sfnClient.describeExecution( 88 | DescribeExecutionRequest.builder().executionArn(startResponse.executionArn()).build()); 89 | return execution.status() == ExecutionStatus.SUCCEEDED; 90 | }); 91 | 92 | DescribeExecutionResponse finalExecution = sfnClient.describeExecution( 93 | DescribeExecutionRequest.builder().executionArn(startResponse.executionArn()).build()); 94 | 95 | assertThat(finalExecution.status()).isEqualTo(ExecutionStatus.SUCCEEDED); 96 | assertThat(finalExecution.output()).isEqualTo("\"Hello World!\""); 97 | } 98 | } 99 | 100 | private String createIamRole() { 101 | try (IamClient iamClient = IamClient.builder() 102 | .endpointOverride(localstack.getEndpoint()) 103 | .credentialsProvider(StaticCredentialsProvider 104 | .create(AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()))) 105 | .region(Region.of(localstack.getRegion())) 106 | .build()) { 107 | 108 | String trustPolicy = """ 109 | { 110 | "Version": "2012-10-17", 111 | "Statement": [ 112 | { 113 | "Effect": "Allow", 114 | "Principal": { 115 | "Service": "states.amazonaws.com" 116 | }, 117 | "Action": "sts:AssumeRole" 118 | } 119 | ] 120 | } 121 | """; 122 | 123 | CreateRoleRequest roleRequest = CreateRoleRequest.builder() 124 | .roleName("StepFunctionsRole") 125 | .assumeRolePolicyDocument(trustPolicy) 126 | .build(); 127 | 128 | CreateRoleResponse roleResponse = iamClient.createRole(roleRequest); 129 | return roleResponse.role().arn(); 130 | } 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /lambda/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.5.9 9 | 10 | 11 | com.example 12 | lambda 13 | 0.0.1-SNAPSHOT 14 | lambda 15 | Demo project for Spring Boot 16 | 17 | 25 18 | 2025.0.0-RC1 19 | 1.0.29.RELEASE 20 | 2.39.3 21 | 1.21.4 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-function-adapter-aws 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-data-jdbc 35 | 36 | 37 | org.flywaydb 38 | flyway-core 39 | 40 | 41 | org.flywaydb 42 | flyway-database-postgresql 43 | 44 | 45 | 46 | org.postgresql 47 | postgresql 48 | runtime 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-test 53 | test 54 | 55 | 56 | org.testcontainers 57 | junit-jupiter 58 | test 59 | 60 | 61 | org.testcontainers 62 | localstack 63 | test 64 | 65 | 66 | org.testcontainers 67 | postgresql 68 | test 69 | 70 | 71 | software.amazon.awssdk 72 | lambda 73 | 74 | 75 | org.apache.maven.shared 76 | maven-invoker 77 | 3.2.0 78 | test 79 | 80 | 81 | io.rest-assured 82 | rest-assured 83 | test 84 | 85 | 86 | 87 | 88 | 89 | org.springframework.cloud 90 | spring-cloud-dependencies 91 | ${spring-cloud.version} 92 | pom 93 | import 94 | 95 | 96 | software.amazon.awssdk 97 | bom 98 | ${aws.java.sdk.version} 99 | pom 100 | import 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | org.apache.maven.plugins 109 | maven-deploy-plugin 110 | 111 | true 112 | 113 | 114 | 115 | org.springframework.boot 116 | spring-boot-maven-plugin 117 | 118 | 119 | org.springframework.boot.experimental 120 | spring-boot-thin-layout 121 | 1.0.29.RELEASE 122 | 123 | 124 | 125 | 126 | org.apache.maven.plugins 127 | maven-shade-plugin 128 | 3.2.4 129 | 130 | false 131 | true 132 | aws 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | spring-milestones 141 | Spring Milestones 142 | https://repo.spring.io/milestone 143 | 144 | false 145 | 146 | 147 | 148 | 149 | 150 | spring-milestones 151 | Spring Milestones 152 | https://repo.spring.io/milestone 153 | 154 | false 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /lambda/src/test/java/com/example/springcloudfunctionaws/SpringCloudFunctionAwsApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.springcloudfunctionaws; 2 | 3 | import io.restassured.RestAssured; 4 | import org.apache.commons.io.FileUtils; 5 | import org.apache.maven.shared.invoker.DefaultInvocationRequest; 6 | import org.apache.maven.shared.invoker.DefaultInvoker; 7 | import org.apache.maven.shared.invoker.MavenInvocationException; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.util.StringUtils; 10 | import org.testcontainers.containers.Network; 11 | import org.testcontainers.containers.PostgreSQLContainer; 12 | import org.testcontainers.containers.localstack.LocalStackContainer; 13 | import org.testcontainers.junit.jupiter.Container; 14 | import org.testcontainers.junit.jupiter.Testcontainers; 15 | import org.testcontainers.utility.DockerImageName; 16 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 17 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; 18 | import software.amazon.awssdk.core.SdkBytes; 19 | import software.amazon.awssdk.regions.Region; 20 | import software.amazon.awssdk.services.lambda.LambdaClient; 21 | import software.amazon.awssdk.services.lambda.model.CreateFunctionRequest; 22 | import software.amazon.awssdk.services.lambda.model.CreateFunctionUrlConfigRequest; 23 | import software.amazon.awssdk.services.lambda.model.Environment; 24 | import software.amazon.awssdk.services.lambda.model.FunctionCode; 25 | import software.amazon.awssdk.services.lambda.model.FunctionUrlAuthType; 26 | import software.amazon.awssdk.services.lambda.model.GetFunctionConfigurationRequest; 27 | import software.amazon.awssdk.services.lambda.model.PackageType; 28 | import software.amazon.awssdk.services.lambda.model.Runtime; 29 | 30 | import java.io.File; 31 | import java.io.IOException; 32 | import java.nio.file.Paths; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Properties; 36 | 37 | import static org.assertj.core.api.Assertions.assertThat; 38 | 39 | @Testcontainers 40 | class SpringCloudFunctionAwsApplicationTests { 41 | 42 | static String jar = buildJar(); 43 | 44 | static Network network = Network.newNetwork(); 45 | 46 | @Container 47 | static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15-alpine").withNetwork(network) 48 | .withNetworkAliases("postgres"); 49 | 50 | @Container 51 | static LocalStackContainer localstack = new LocalStackContainer( 52 | DockerImageName.parse("localstack/localstack:4.12.0")) 53 | .withNetwork(network) 54 | .withEnv("LOCALSTACK_HOST", "localhost.localstack.cloud") 55 | .withEnv("LAMBDA_DOCKER_NETWORK", ((Network.NetworkImpl) network).getName()) 56 | .withNetworkAliases("localstack"); 57 | 58 | static String buildJar() { 59 | try { 60 | var properties = new Properties(); 61 | properties.setProperty("skipTests", "true"); 62 | 63 | var defaultInvocationRequest = new DefaultInvocationRequest(); 64 | if (StringUtils.hasText(System.getenv("MAVEN_HOME"))) { 65 | defaultInvocationRequest.setMavenHome(new File(System.getenv("MAVEN_HOME"))); 66 | } 67 | defaultInvocationRequest.setJavaHome(new File(System.getenv("JAVA_HOME"))); 68 | defaultInvocationRequest.setPomFile(Paths.get("pom.xml").toFile()); 69 | defaultInvocationRequest.setGoals(List.of("package")); 70 | defaultInvocationRequest.setProperties(properties); 71 | var appInvoker = new DefaultInvoker(); 72 | appInvoker.execute(defaultInvocationRequest); 73 | } 74 | catch (MavenInvocationException e) { 75 | throw new RuntimeException("Could not build jar", e); 76 | } 77 | return "target/lambda-0.0.1-SNAPSHOT-aws.jar"; 78 | } 79 | 80 | @Test 81 | void contextLoads() throws IOException { 82 | // Setup for lambda 83 | var fnName = "uppercase-fn"; 84 | var envVars = Map.ofEntries(Map.entry("SPRING_DATASOURCE_URL", "jdbc:postgresql://postgres:5432/test"), 85 | Map.entry("SPRING_DATASOURCE_USERNAME", "test"), Map.entry("SPRING_DATASOURCE_PASSWORD", "test")); 86 | var createFunctionRequest = CreateFunctionRequest.builder() 87 | .functionName(fnName) 88 | .runtime(Runtime.JAVA25) 89 | .role("arn:aws:iam::123456789012:role/irrelevant") 90 | .packageType(PackageType.ZIP) 91 | .code(FunctionCode.builder() 92 | .zipFile(SdkBytes.fromByteArray(FileUtils.readFileToByteArray(new File(jar)))) 93 | .build()) 94 | .timeout(10) 95 | .handler("org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest") 96 | .environment(Environment.builder().variables(envVars).build()) 97 | .build(); 98 | 99 | // Lambda client pointing to LocalStack 100 | try (var lambdaClient = LambdaClient.builder() 101 | .region(Region.of(localstack.getRegion())) 102 | .credentialsProvider(StaticCredentialsProvider 103 | .create(AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey()))) 104 | .endpointOverride(localstack.getEndpoint()) 105 | .build()) { 106 | 107 | // Create a function and wait for it to be active 108 | var createFunctionResponse = lambdaClient.createFunction(createFunctionRequest); 109 | var waiterResponse = lambdaClient.waiter() 110 | .waitUntilFunctionActive(GetFunctionConfigurationRequest.builder().functionName(fnName).build()); 111 | 112 | // Create a URL for the function and replace it 4566 by the random port 113 | var createFunctionUrlConfigRequest = CreateFunctionUrlConfigRequest.builder() 114 | .functionName(fnName) 115 | .authType(FunctionUrlAuthType.NONE) 116 | .build(); 117 | var createFunctionUrlConfigResponse = lambdaClient.createFunctionUrlConfig(createFunctionUrlConfigRequest); 118 | var functionUrl = createFunctionUrlConfigResponse.functionUrl() 119 | .replace("" + 4566, "" + localstack.getMappedPort(4566)); 120 | 121 | // Test the function 122 | var body = RestAssured.given().body(""" 123 | {"name": "profile"} 124 | """).get(functionUrl).prettyPeek().andReturn().body(); 125 | assertThat(body.asString()).isEqualTo("4"); 126 | } 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /cognito/src/test/java/com/example/cognito/CognitoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.cognito; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.boot.test.web.server.LocalServerPort; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.test.context.DynamicPropertyRegistry; 10 | import org.springframework.test.context.DynamicPropertySource; 11 | import org.springframework.web.client.HttpClientErrorException; 12 | import org.springframework.web.client.RestClient; 13 | import org.testcontainers.containers.localstack.LocalStackContainer; 14 | import org.testcontainers.junit.jupiter.Container; 15 | import org.testcontainers.junit.jupiter.Testcontainers; 16 | import org.testcontainers.utility.DockerImageName; 17 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 18 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; 19 | import software.amazon.awssdk.regions.Region; 20 | import software.amazon.awssdk.services.cognitoidentityprovider.CognitoIdentityProviderClient; 21 | import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminCreateUserRequest; 22 | import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminResetUserPasswordRequest; 23 | import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminSetUserPasswordRequest; 24 | import software.amazon.awssdk.services.cognitoidentityprovider.model.AttributeType; 25 | import software.amazon.awssdk.services.cognitoidentityprovider.model.AuthFlowType; 26 | import software.amazon.awssdk.services.cognitoidentityprovider.model.CreateUserPoolClientRequest; 27 | import software.amazon.awssdk.services.cognitoidentityprovider.model.CreateUserPoolRequest; 28 | import software.amazon.awssdk.services.cognitoidentityprovider.model.InitiateAuthRequest; 29 | 30 | import java.util.Map; 31 | 32 | import static org.assertj.core.api.Assertions.assertThat; 33 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 34 | 35 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 36 | @Testcontainers 37 | @EnabledIfEnvironmentVariable(named = "LOCALSTACK_AUTH_TOKEN", matches = ".+") 38 | class CognitoApplicationTests { 39 | 40 | @Container 41 | private static final LocalStackContainer localstack = new LocalStackContainer( 42 | DockerImageName.parse("localstack/localstack-pro:4.12.0")) 43 | .withEnv("SERVICES", "cognito-idp") 44 | .withEnv("LOCALSTACK_AUTH_TOKEN", System.getenv("LOCALSTACK_AUTH_TOKEN")); 45 | 46 | private static String userPoolId; 47 | 48 | private static String appClientId; 49 | 50 | private static String accessToken; 51 | 52 | @LocalServerPort 53 | private int port; 54 | 55 | @BeforeAll 56 | static void beforeAll() { 57 | var credentialsProvider = StaticCredentialsProvider 58 | .create(AwsBasicCredentials.create(localstack.getAccessKey(), localstack.getSecretKey())); 59 | try (var cognitoClient = CognitoIdentityProviderClient.builder() 60 | .credentialsProvider(credentialsProvider) 61 | .region(Region.of(localstack.getRegion())) 62 | .endpointOverride(localstack.getEndpoint()) 63 | .build()) { 64 | 65 | var userPoolResponse = cognitoClient 66 | .createUserPool(CreateUserPoolRequest.builder().poolName("awspring-test").build()); 67 | userPoolId = userPoolResponse.userPool().id(); 68 | 69 | var userPoolClientResponse = cognitoClient.createUserPoolClient(CreateUserPoolClientRequest.builder() 70 | .clientName("awspring-test-client") 71 | .userPoolId(userPoolId) 72 | .build()); 73 | appClientId = userPoolClientResponse.userPoolClient().clientId(); 74 | 75 | cognitoClient.adminCreateUser(AdminCreateUserRequest.builder() 76 | .userPoolId(userPoolId) 77 | .username("testuser@test.com") 78 | .temporaryPassword("testP@ssw0rd") 79 | .userAttributes(AttributeType.builder().name("email").value("testuser@test.com").build(), 80 | AttributeType.builder().name("email_verified").value("true").build()) 81 | .build()); 82 | 83 | cognitoClient.adminResetUserPassword(AdminResetUserPasswordRequest.builder() 84 | .username("testuser@test.com") 85 | .userPoolId(userPoolId) 86 | .build()); 87 | 88 | cognitoClient.adminSetUserPassword(AdminSetUserPasswordRequest.builder() 89 | .username("testuser@test.com") 90 | .password("testP4ssw*rd") 91 | .userPoolId(userPoolId) 92 | .permanent(Boolean.TRUE) 93 | .build()); 94 | 95 | var initiateAuthResponse = cognitoClient.initiateAuth(InitiateAuthRequest.builder() 96 | .authFlow(AuthFlowType.USER_PASSWORD_AUTH) 97 | .clientId(appClientId) 98 | .authParameters(Map.of("USERNAME", "testuser@test.com", "PASSWORD", "testP4ssw*rd")) 99 | .build()); 100 | accessToken = initiateAuthResponse.authenticationResult().accessToken(); 101 | } 102 | } 103 | 104 | @DynamicPropertySource 105 | static void dynamicProperties(DynamicPropertyRegistry registry) { 106 | // registry.add("spring.security.oauth2.resourceserver.jwt.issuer-uri", () -> 107 | // localstack.getEndpoint() + "/" + userPoolId); 108 | registry.add("spring.security.oauth2.resourceserver.jwt.jwk-set-uri", 109 | () -> localstack.getEndpoint() + "/" + userPoolId + "/.well-known/jwks.json"); 110 | } 111 | 112 | @Test 113 | void contextLoads() { 114 | var appBaseUrl = "http://localhost:" + this.port; 115 | 116 | var restClient = RestClient.builder().baseUrl(appBaseUrl).build(); 117 | 118 | var unsecuredResponse = restClient.get().uri(appBaseUrl).retrieve().toBodilessEntity(); 119 | assertThat(unsecuredResponse.getStatusCode()).isEqualTo(HttpStatus.OK); 120 | 121 | assertThatExceptionOfType(HttpClientErrorException.Unauthorized.class) 122 | .isThrownBy(() -> restClient.get().uri("/topsecret").retrieve().toBodilessEntity()); 123 | 124 | var securedResponse = restClient.get() 125 | .uri("/topsecret") 126 | .headers(h -> h.setBearerAuth(accessToken)) 127 | .retrieve() 128 | .toBodilessEntity(); 129 | assertThat(securedResponse.getStatusCode()).isEqualTo(HttpStatus.OK); 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | <# : batch portion 2 | @REM ---------------------------------------------------------------------------- 3 | @REM Licensed to the Apache Software Foundation (ASF) under one 4 | @REM or more contributor license agreements. See the NOTICE file 5 | @REM distributed with this work for additional information 6 | @REM regarding copyright ownership. The ASF licenses this file 7 | @REM to you under the Apache License, Version 2.0 (the 8 | @REM "License"); you may not use this file except in compliance 9 | @REM with the License. You may obtain a copy of the License at 10 | @REM 11 | @REM http://www.apache.org/licenses/LICENSE-2.0 12 | @REM 13 | @REM Unless required by applicable law or agreed to in writing, 14 | @REM software distributed under the License is distributed on an 15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | @REM KIND, either express or implied. See the License for the 17 | @REM specific language governing permissions and limitations 18 | @REM under the License. 19 | @REM ---------------------------------------------------------------------------- 20 | 21 | @REM ---------------------------------------------------------------------------- 22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 23 | @REM 24 | @REM Optional ENV vars 25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution 26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output 28 | @REM ---------------------------------------------------------------------------- 29 | 30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) 31 | @SET __MVNW_CMD__= 32 | @SET __MVNW_ERROR__= 33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath% 34 | @SET PSModulePath= 35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( 36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) 37 | ) 38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE% 39 | @SET __MVNW_PSMODULEP_SAVE= 40 | @SET __MVNW_ARG0_NAME__= 41 | @SET MVNW_USERNAME= 42 | @SET MVNW_PASSWORD= 43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) 44 | @echo Cannot start maven from wrapper >&2 && exit /b 1 45 | @GOTO :EOF 46 | : end batch / begin powershell #> 47 | 48 | $ErrorActionPreference = "Stop" 49 | if ($env:MVNW_VERBOSE -eq "true") { 50 | $VerbosePreference = "Continue" 51 | } 52 | 53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties 54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl 55 | if (!$distributionUrl) { 56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 57 | } 58 | 59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { 60 | "maven-mvnd-*" { 61 | $USE_MVND = $true 62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" 63 | $MVN_CMD = "mvnd.cmd" 64 | break 65 | } 66 | default { 67 | $USE_MVND = $false 68 | $MVN_CMD = $script -replace '^mvnw','mvn' 69 | break 70 | } 71 | } 72 | 73 | # apply MVNW_REPOURL and calculate MAVEN_HOME 74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 75 | if ($env:MVNW_REPOURL) { 76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } 77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" 78 | } 79 | $distributionUrlName = $distributionUrl -replace '^.*/','' 80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' 81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" 82 | if ($env:MAVEN_USER_HOME) { 83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" 84 | } 85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' 86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" 87 | 88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { 89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" 90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 91 | exit $? 92 | } 93 | 94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { 95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" 96 | } 97 | 98 | # prepare tmp dir 99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile 100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" 101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null 102 | trap { 103 | if ($TMP_DOWNLOAD_DIR.Exists) { 104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 106 | } 107 | } 108 | 109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null 110 | 111 | # Download and Install Apache Maven 112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 113 | Write-Verbose "Downloading from: $distributionUrl" 114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 115 | 116 | $webclient = New-Object System.Net.WebClient 117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { 118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) 119 | } 120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null 122 | 123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum 125 | if ($distributionSha256Sum) { 126 | if ($USE_MVND) { 127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." 128 | } 129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash 130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { 131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." 132 | } 133 | } 134 | 135 | # unzip and move 136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null 137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null 138 | try { 139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null 140 | } catch { 141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { 142 | Write-Error "fail to move MAVEN_HOME" 143 | } 144 | } finally { 145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 147 | } 148 | 149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 150 | -------------------------------------------------------------------------------- /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 | # Apache Maven Wrapper startup batch script, version 3.3.2 23 | # 24 | # Optional ENV vars 25 | # ----------------- 26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source 27 | # MVNW_REPOURL - repo url base for downloading maven distribution 28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output 30 | # ---------------------------------------------------------------------------- 31 | 32 | set -euf 33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x 34 | 35 | # OS specific support. 36 | native_path() { printf %s\\n "$1"; } 37 | case "$(uname)" in 38 | CYGWIN* | MINGW*) 39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" 40 | native_path() { cygpath --path --windows "$1"; } 41 | ;; 42 | esac 43 | 44 | # set JAVACMD and JAVACCMD 45 | set_java_home() { 46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched 47 | if [ -n "${JAVA_HOME-}" ]; then 48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 49 | # IBM's JDK on AIX uses strange locations for the executables 50 | JAVACMD="$JAVA_HOME/jre/sh/java" 51 | JAVACCMD="$JAVA_HOME/jre/sh/javac" 52 | else 53 | JAVACMD="$JAVA_HOME/bin/java" 54 | JAVACCMD="$JAVA_HOME/bin/javac" 55 | 56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then 57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 59 | return 1 60 | fi 61 | fi 62 | else 63 | JAVACMD="$( 64 | 'set' +e 65 | 'unset' -f command 2>/dev/null 66 | 'command' -v java 67 | )" || : 68 | JAVACCMD="$( 69 | 'set' +e 70 | 'unset' -f command 2>/dev/null 71 | 'command' -v javac 72 | )" || : 73 | 74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then 75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 76 | return 1 77 | fi 78 | fi 79 | } 80 | 81 | # hash string like Java String::hashCode 82 | hash_string() { 83 | str="${1:-}" h=0 84 | while [ -n "$str" ]; do 85 | char="${str%"${str#?}"}" 86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) 87 | str="${str#?}" 88 | done 89 | printf %x\\n $h 90 | } 91 | 92 | verbose() { :; } 93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } 94 | 95 | die() { 96 | printf %s\\n "$1" >&2 97 | exit 1 98 | } 99 | 100 | trim() { 101 | # MWRAPPER-139: 102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. 103 | # Needed for removing poorly interpreted newline sequences when running in more 104 | # exotic environments such as mingw bash on Windows. 105 | printf "%s" "${1}" | tr -d '[:space:]' 106 | } 107 | 108 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties 109 | while IFS="=" read -r key value; do 110 | case "${key-}" in 111 | distributionUrl) distributionUrl=$(trim "${value-}") ;; 112 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; 113 | esac 114 | done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" 115 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" 116 | 117 | case "${distributionUrl##*/}" in 118 | maven-mvnd-*bin.*) 119 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ 120 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in 121 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; 122 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; 123 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; 124 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;; 125 | *) 126 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 127 | distributionPlatform=linux-amd64 128 | ;; 129 | esac 130 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" 131 | ;; 132 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; 133 | *) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; 134 | esac 135 | 136 | # apply MVNW_REPOURL and calculate MAVEN_HOME 137 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 138 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" 139 | distributionUrlName="${distributionUrl##*/}" 140 | distributionUrlNameMain="${distributionUrlName%.*}" 141 | distributionUrlNameMain="${distributionUrlNameMain%-bin}" 142 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" 143 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" 144 | 145 | exec_maven() { 146 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : 147 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" 148 | } 149 | 150 | if [ -d "$MAVEN_HOME" ]; then 151 | verbose "found existing MAVEN_HOME at $MAVEN_HOME" 152 | exec_maven "$@" 153 | fi 154 | 155 | case "${distributionUrl-}" in 156 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; 157 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; 158 | esac 159 | 160 | # prepare tmp dir 161 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then 162 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } 163 | trap clean HUP INT TERM EXIT 164 | else 165 | die "cannot create temp dir" 166 | fi 167 | 168 | mkdir -p -- "${MAVEN_HOME%/*}" 169 | 170 | # Download and Install Apache Maven 171 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 172 | verbose "Downloading from: $distributionUrl" 173 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 174 | 175 | # select .zip or .tar.gz 176 | if ! command -v unzip >/dev/null; then 177 | distributionUrl="${distributionUrl%.zip}.tar.gz" 178 | distributionUrlName="${distributionUrl##*/}" 179 | fi 180 | 181 | # verbose opt 182 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' 183 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v 184 | 185 | # normalize http auth 186 | case "${MVNW_PASSWORD:+has-password}" in 187 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;; 188 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; 189 | esac 190 | 191 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then 192 | verbose "Found wget ... using wget" 193 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" 194 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then 195 | verbose "Found curl ... using curl" 196 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" 197 | elif set_java_home; then 198 | verbose "Falling back to use Java to download" 199 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" 200 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" 201 | cat >"$javaSource" <<-END 202 | public class Downloader extends java.net.Authenticator 203 | { 204 | protected java.net.PasswordAuthentication getPasswordAuthentication() 205 | { 206 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); 207 | } 208 | public static void main( String[] args ) throws Exception 209 | { 210 | setDefault( new Downloader() ); 211 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); 212 | } 213 | } 214 | END 215 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java 216 | verbose " - Compiling Downloader.java ..." 217 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" 218 | verbose " - Running Downloader.java ..." 219 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" 220 | fi 221 | 222 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 223 | if [ -n "${distributionSha256Sum-}" ]; then 224 | distributionSha256Result=false 225 | if [ "$MVN_CMD" = mvnd.sh ]; then 226 | echo "Checksum validation is not supported for maven-mvnd." >&2 227 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 228 | exit 1 229 | elif command -v sha256sum >/dev/null; then 230 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then 231 | distributionSha256Result=true 232 | fi 233 | elif command -v shasum >/dev/null; then 234 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then 235 | distributionSha256Result=true 236 | fi 237 | else 238 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 239 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 240 | exit 1 241 | fi 242 | if [ $distributionSha256Result = false ]; then 243 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 244 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 245 | exit 1 246 | fi 247 | fi 248 | 249 | # unzip and move 250 | if command -v unzip >/dev/null; then 251 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" 252 | else 253 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" 254 | fi 255 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" 256 | mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" 257 | 258 | clean || : 259 | exec_maven "$@" 260 | --------------------------------------------------------------------------------