Skip to content

Commit b80f833

Browse files
committed
Generate test app when spring cloud azure storage is selected
Spring Cloud Azure 5.16.0 supports Testcontainers and Docker Compose Service Connection for Blob Storage and Queue. * Add `spring-cloud-azure-docker-compose` or `spring-cloud-azure-testcontainers` dependency * Configure `docker-compose.yaml` with image `mcr.microsoft.com/azure-storage/azurite:latest` * Configure `GenericContainer` with image `mcr.microsoft.com/azure-storage/azurite:latest`
1 parent ddf254a commit b80f833

12 files changed

+451
-0
lines changed

start-site/src/main/java/io/spring/start/site/container/SimpleDockerServiceResolver.java

+8
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public SimpleDockerServiceResolver() {
3737
this.dockerServices.put("activeMQ", activeMQ());
3838
this.dockerServices.put("activeMQClassic", activeMQClassic());
3939
this.dockerServices.put("artemis", artemis());
40+
this.dockerServices.put("azurite", azurite());
4041
this.dockerServices.put("cassandra", cassandra());
4142
this.dockerServices.put("chroma", chroma());
4243
this.dockerServices.put("elasticsearch", elasticsearch());
@@ -81,6 +82,13 @@ private static DockerService artemis() {
8182
.build();
8283
}
8384

85+
private static DockerService azurite() {
86+
return DockerService.withImageAndTag("mcr.microsoft.com/azure-storage/azurite")
87+
.website("https://github.com/Azure/Azurite?tab=readme-ov-file#dockerhub")
88+
.ports(10000, 10001, 10002)
89+
.build();
90+
}
91+
8492
private static DockerService cassandra() {
8593
return DockerService.withImageAndTag("cassandra")
8694
.website("https://hub.docker.com/_/cassandra")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.start.site.extension.dependency.springazure;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import io.spring.initializr.generator.project.ProjectDescription;
26+
27+
import org.springframework.context.annotation.Conditional;
28+
29+
/**
30+
* Condition that matches when a {@link ProjectDescription} defines a dependency on Spring
31+
* Azure. A generated project may ultimately define a different set of dependencies
32+
* according to the contributors that have been executed. To contribute to the project
33+
* according to the real set, prefer querying the model itself rather than using this
34+
* condition.
35+
*
36+
* @author Eddú Meléndez
37+
*/
38+
@Retention(RetentionPolicy.RUNTIME)
39+
@Target({ ElementType.TYPE, ElementType.METHOD })
40+
@Documented
41+
@Conditional(OnRequestedSpringAzureDependencyCondition.class)
42+
@interface ConditionalOnRequestedSpringAzureDependency {
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.start.site.extension.dependency.springazure;
18+
19+
import io.spring.initializr.generator.buildsystem.Dependency;
20+
import io.spring.initializr.generator.condition.ProjectGenerationCondition;
21+
import io.spring.initializr.generator.project.ProjectDescription;
22+
23+
import org.springframework.context.annotation.ConditionContext;
24+
import org.springframework.core.type.AnnotatedTypeMetadata;
25+
26+
/**
27+
* {@link ProjectGenerationCondition} implementation for
28+
* {@link ConditionalOnRequestedSpringAzureDependency}.
29+
*
30+
* @author Eddú Meléndez
31+
*/
32+
class OnRequestedSpringAzureDependencyCondition extends ProjectGenerationCondition {
33+
34+
@Override
35+
protected boolean matches(ProjectDescription description, ConditionContext context,
36+
AnnotatedTypeMetadata metadata) {
37+
for (Dependency dependency : description.getRequestedDependencies().values()) {
38+
if (dependency.getGroupId().equals("com.azure.spring")) {
39+
return true;
40+
}
41+
}
42+
return false;
43+
}
44+
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.start.site.extension.dependency.springazure;
18+
19+
import io.spring.initializr.generator.buildsystem.Build;
20+
import io.spring.initializr.generator.buildsystem.Dependency;
21+
import io.spring.initializr.generator.buildsystem.DependencyScope;
22+
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
23+
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
24+
import io.spring.initializr.generator.spring.build.BuildCustomizer;
25+
26+
import org.springframework.context.annotation.Bean;
27+
28+
/**
29+
* Configuration for generation of projects that depend on Spring Azure Docker Compose.
30+
*
31+
* @author Eddú Meléndez
32+
*/
33+
@ProjectGenerationConfiguration
34+
@ConditionalOnRequestedDependency("docker-compose")
35+
@ConditionalOnRequestedSpringAzureDependency
36+
class SpringAzureDockerComposeProjectGenerationConfiguration {
37+
38+
@Bean
39+
BuildCustomizer<Build> springAiTestcontainersBuildCustomizer() {
40+
return (build) -> build.dependencies()
41+
.add("spring-azure-docker-compose",
42+
Dependency.withCoordinates("com.azure.spring", "spring-cloud-azure-docker-compose")
43+
.scope(DependencyScope.TEST_COMPILE));
44+
}
45+
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.start.site.extension.dependency.springazure;
18+
19+
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
20+
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
21+
import io.spring.start.site.container.ComposeFileCustomizer;
22+
import io.spring.start.site.container.DockerServiceResolver;
23+
import io.spring.start.site.container.ServiceConnections;
24+
import io.spring.start.site.container.ServiceConnectionsCustomizer;
25+
26+
import org.springframework.context.annotation.Bean;
27+
28+
/**
29+
* Configuration for generation of projects that depend on Azurite (Storage Emulator).
30+
*
31+
* @author Eddú Meléndez
32+
*/
33+
@ProjectGenerationConfiguration
34+
@ConditionalOnRequestedDependency("azure-storage")
35+
class SpringAzureStorageProjectGenerationConfiguration {
36+
37+
@Bean
38+
@ConditionalOnRequestedDependency("testcontainers")
39+
ServiceConnectionsCustomizer chromaServiceConnectionsCustomizer(DockerServiceResolver serviceResolver) {
40+
return (serviceConnections) -> serviceResolver.doWith("azurite",
41+
(service) -> serviceConnections.addServiceConnection(ServiceConnections.ServiceConnection
42+
.ofGenericContainer("azurite", service, "azure-storage/azurite")));
43+
}
44+
45+
@Bean
46+
@ConditionalOnRequestedDependency("docker-compose")
47+
ComposeFileCustomizer chromaComposeFileCustomizer(DockerServiceResolver serviceResolver) {
48+
return (composeFile) -> serviceResolver.doWith("azurite",
49+
(service) -> composeFile.services().add("azurite", service));
50+
}
51+
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.start.site.extension.dependency.springazure;
18+
19+
import io.spring.initializr.generator.buildsystem.Build;
20+
import io.spring.initializr.generator.buildsystem.Dependency;
21+
import io.spring.initializr.generator.buildsystem.DependencyScope;
22+
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
23+
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
24+
import io.spring.initializr.generator.spring.build.BuildCustomizer;
25+
26+
import org.springframework.context.annotation.Bean;
27+
28+
/**
29+
* Configuration for generation of projects that depend on Spring Azure Testcontainers.
30+
*
31+
* @author Eddú Meléndez
32+
*/
33+
@ProjectGenerationConfiguration
34+
@ConditionalOnRequestedDependency("testcontainers")
35+
@ConditionalOnRequestedSpringAzureDependency
36+
class SpringAzureTestcontainersProjectGenerationConfiguration {
37+
38+
@Bean
39+
BuildCustomizer<Build> springAiTestcontainersBuildCustomizer() {
40+
return (build) -> build.dependencies()
41+
.add("spring-azure-testcontainers",
42+
Dependency.withCoordinates("com.azure.spring", "spring-cloud-azure-testcontainers")
43+
.scope(DependencyScope.TEST_COMPILE));
44+
}
45+
46+
}

start-site/src/main/resources/META-INF/spring.factories

+3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ io.spring.start.site.extension.dependency.springai.SpringAiQdrantProjectGenerati
3535
io.spring.start.site.extension.dependency.springai.SpringAiTestcontainersProjectGenerationConfiguration,\
3636
io.spring.start.site.extension.dependency.springai.SpringAiWeaviateProjectGenerationConfiguration,\
3737
io.spring.start.site.extension.dependency.springamqp.SpringAmqpProjectGenerationConfiguration,\
38+
io.spring.start.site.extension.dependency.springazure.SpringAzureDockerComposeProjectGenerationConfiguration,\
39+
io.spring.start.site.extension.dependency.springazure.SpringAzureStorageProjectGenerationConfiguration,\
3840
io.spring.start.site.extension.dependency.springazure.SpringAzureProjectGenerationConfiguration,\
41+
io.spring.start.site.extension.dependency.springazure.SpringAzureTestcontainersProjectGenerationConfiguration,\
3942
io.spring.start.site.extension.dependency.springboot.SpringBootProjectGenerationConfiguration,\
4043
io.spring.start.site.extension.dependency.springcloud.SpringCloudProjectGenerationConfiguration,\
4144
io.spring.start.site.extension.dependency.springdata.SpringDataProjectGenerationConfiguration,\
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.start.site.extension.dependency.springazure;
18+
19+
import io.spring.initializr.generator.buildsystem.Dependency;
20+
import io.spring.initializr.generator.project.MutableProjectDescription;
21+
import io.spring.initializr.generator.project.ProjectDescription;
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.context.annotation.ConditionContext;
25+
import org.springframework.core.type.AnnotatedTypeMetadata;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.mockito.Mockito.mock;
29+
30+
/**
31+
* Tests for {@link OnRequestedSpringAzureDependencyCondition}.
32+
*
33+
* @author Eddú Meléndez
34+
*/
35+
class OnRequestedSpringAzureDependencyConditionTests {
36+
37+
@Test
38+
void shouldMatchIfSpringAzureHasBeenRequested() {
39+
OnRequestedSpringAzureDependencyCondition condition = new OnRequestedSpringAzureDependencyCondition();
40+
MutableProjectDescription description = new MutableProjectDescription();
41+
description.addDependency("azure-storage",
42+
Dependency.withCoordinates("com.azure.spring", "spring-cloud-azure-starter-storage").build());
43+
assertThat(matches(condition, description)).isTrue();
44+
}
45+
46+
@Test
47+
void shouldNotMatchIfSpringAzureHasNotBeenRequested() {
48+
OnRequestedSpringAzureDependencyCondition condition = new OnRequestedSpringAzureDependencyCondition();
49+
MutableProjectDescription description = new MutableProjectDescription();
50+
description.addDependency("devtools",
51+
Dependency.withCoordinates("org.springframework.boot", "spring-boot-devtools").build());
52+
assertThat(matches(condition, description)).isFalse();
53+
}
54+
55+
private static boolean matches(OnRequestedSpringAzureDependencyCondition condition,
56+
ProjectDescription description) {
57+
return condition.matches(description, mock(ConditionContext.class), mock(AnnotatedTypeMetadata.class));
58+
}
59+
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.spring.start.site.extension.dependency.springazure;
18+
19+
import io.spring.initializr.web.project.ProjectRequest;
20+
import io.spring.start.site.extension.AbstractExtensionTests;
21+
import org.junit.jupiter.api.Test;
22+
23+
import static org.assertj.core.api.Assertions.assertThat;
24+
25+
/**
26+
* Tests for {@link SpringAzureDockerComposeProjectGenerationConfiguration}.
27+
*
28+
* @author Eddú Meléndez
29+
*/
30+
class SpringAzureDockerComposeProjectGenerationConfigurationTests extends AbstractExtensionTests {
31+
32+
@Test
33+
void springAzureDockerComposeDependencyIsAdded() {
34+
ProjectRequest projectRequest = createProjectRequest("docker-compose", "azure-storage");
35+
assertThat(mavenPom(projectRequest)).hasDependency("com.azure.spring", "spring-cloud-azure-docker-compose",
36+
null, "test");
37+
}
38+
39+
@Test
40+
void shouldNotAddDockerComposeTestcontainersDependencyIfNoSpringAzureDependencyIsSelected() {
41+
ProjectRequest projectRequest = createProjectRequest("docker-compose", "web");
42+
assertThat(mavenPom(projectRequest)).doesNotHaveDependency("com.azure.spring",
43+
"spring-cloud-azure-docker-compose");
44+
}
45+
46+
}

0 commit comments

Comments
 (0)