Skip to content

Commit

Permalink
Generate test app when spring cloud azure storage is selected
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
eddumelendez committed Sep 11, 2024
1 parent ddf254a commit 5762b63
Show file tree
Hide file tree
Showing 12 changed files with 451 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public SimpleDockerServiceResolver() {
this.dockerServices.put("activeMQ", activeMQ());
this.dockerServices.put("activeMQClassic", activeMQClassic());
this.dockerServices.put("artemis", artemis());
this.dockerServices.put("azurite", azurite());
this.dockerServices.put("cassandra", cassandra());
this.dockerServices.put("chroma", chroma());
this.dockerServices.put("elasticsearch", elasticsearch());
Expand Down Expand Up @@ -81,6 +82,13 @@ private static DockerService artemis() {
.build();
}

private static DockerService azurite() {
return DockerService.withImageAndTag("mcr.microsoft.com/azure-storage/azurite")
.website("https://github.com/Azure/Azurite?tab=readme-ov-file#dockerhub")
.ports(10000, 10001, 10002)
.build();
}

private static DockerService cassandra() {
return DockerService.withImageAndTag("cassandra")
.website("https://hub.docker.com/_/cassandra")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.spring.start.site.extension.dependency.springazure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import io.spring.initializr.generator.project.ProjectDescription;

import org.springframework.context.annotation.Conditional;

/**
* Condition that matches when a {@link ProjectDescription} defines a dependency on Spring
* Azure. A generated project may ultimately define a different set of dependencies
* according to the contributors that have been executed. To contribute to the project
* according to the real set, prefer querying the model itself rather than using this
* condition.
*
* @author Eddú Meléndez
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnRequestedSpringAzureDependencyCondition.class)
@interface ConditionalOnRequestedSpringAzureDependency {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.spring.start.site.extension.dependency.springazure;

import io.spring.initializr.generator.buildsystem.Dependency;
import io.spring.initializr.generator.condition.ProjectGenerationCondition;
import io.spring.initializr.generator.project.ProjectDescription;

import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
* {@link ProjectGenerationCondition} implementation for
* {@link ConditionalOnRequestedSpringAzureDependency}.
*
* @author Eddú Meléndez
*/
class OnRequestedSpringAzureDependencyCondition extends ProjectGenerationCondition {

@Override
protected boolean matches(ProjectDescription description, ConditionContext context,
AnnotatedTypeMetadata metadata) {
for (Dependency dependency : description.getRequestedDependencies().values()) {
if (dependency.getGroupId().equals("com.azure.spring")) {
return true;
}
}
return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.spring.start.site.extension.dependency.springazure;

import io.spring.initializr.generator.buildsystem.Build;
import io.spring.initializr.generator.buildsystem.Dependency;
import io.spring.initializr.generator.buildsystem.DependencyScope;
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
import io.spring.initializr.generator.spring.build.BuildCustomizer;

import org.springframework.context.annotation.Bean;

/**
* Configuration for generation of projects that depend on Spring Azure Docker Compose.
*
* @author Eddú Meléndez
*/
@ProjectGenerationConfiguration
@ConditionalOnRequestedDependency("docker-compose")
@ConditionalOnRequestedSpringAzureDependency
class SpringAzureDockerComposeProjectGenerationConfiguration {

@Bean
BuildCustomizer<Build> springAzureDockerComposeBuildCustomizer() {
return (build) -> build.dependencies()
.add("spring-azure-docker-compose",
Dependency.withCoordinates("com.azure.spring", "spring-cloud-azure-docker-compose")
.scope(DependencyScope.TEST_COMPILE));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.spring.start.site.extension.dependency.springazure;

import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
import io.spring.start.site.container.ComposeFileCustomizer;
import io.spring.start.site.container.DockerServiceResolver;
import io.spring.start.site.container.ServiceConnections;
import io.spring.start.site.container.ServiceConnectionsCustomizer;

import org.springframework.context.annotation.Bean;

/**
* Configuration for generation of projects that depend on Azurite (Storage Emulator).
*
* @author Eddú Meléndez
*/
@ProjectGenerationConfiguration
@ConditionalOnRequestedDependency("azure-storage")
class SpringAzureStorageProjectGenerationConfiguration {

@Bean
@ConditionalOnRequestedDependency("testcontainers")
ServiceConnectionsCustomizer azureStorageServiceConnectionsCustomizer(DockerServiceResolver serviceResolver) {
return (serviceConnections) -> serviceResolver.doWith("azurite",
(service) -> serviceConnections.addServiceConnection(ServiceConnections.ServiceConnection
.ofGenericContainer("azurite", service, "azure-storage/azurite")));
}

@Bean
@ConditionalOnRequestedDependency("docker-compose")
ComposeFileCustomizer azureStorageComposeFileCustomizer(DockerServiceResolver serviceResolver) {
return (composeFile) -> serviceResolver.doWith("azurite",
(service) -> composeFile.services().add("azurite", service));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.spring.start.site.extension.dependency.springazure;

import io.spring.initializr.generator.buildsystem.Build;
import io.spring.initializr.generator.buildsystem.Dependency;
import io.spring.initializr.generator.buildsystem.DependencyScope;
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
import io.spring.initializr.generator.spring.build.BuildCustomizer;

import org.springframework.context.annotation.Bean;

/**
* Configuration for generation of projects that depend on Spring Azure Testcontainers.
*
* @author Eddú Meléndez
*/
@ProjectGenerationConfiguration
@ConditionalOnRequestedDependency("testcontainers")
@ConditionalOnRequestedSpringAzureDependency
class SpringAzureTestcontainersProjectGenerationConfiguration {

@Bean
BuildCustomizer<Build> springAzureTestcontainersBuildCustomizer() {
return (build) -> build.dependencies()
.add("spring-azure-testcontainers",
Dependency.withCoordinates("com.azure.spring", "spring-cloud-azure-testcontainers")
.scope(DependencyScope.TEST_COMPILE));
}

}
3 changes: 3 additions & 0 deletions start-site/src/main/resources/META-INF/spring.factories
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ io.spring.start.site.extension.dependency.springai.SpringAiQdrantProjectGenerati
io.spring.start.site.extension.dependency.springai.SpringAiTestcontainersProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springai.SpringAiWeaviateProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springamqp.SpringAmqpProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springazure.SpringAzureDockerComposeProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springazure.SpringAzureStorageProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springazure.SpringAzureProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springazure.SpringAzureTestcontainersProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springboot.SpringBootProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springcloud.SpringCloudProjectGenerationConfiguration,\
io.spring.start.site.extension.dependency.springdata.SpringDataProjectGenerationConfiguration,\
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.spring.start.site.extension.dependency.springazure;

import io.spring.initializr.generator.buildsystem.Dependency;
import io.spring.initializr.generator.project.MutableProjectDescription;
import io.spring.initializr.generator.project.ProjectDescription;
import org.junit.jupiter.api.Test;

import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

/**
* Tests for {@link OnRequestedSpringAzureDependencyCondition}.
*
* @author Eddú Meléndez
*/
class OnRequestedSpringAzureDependencyConditionTests {

@Test
void shouldMatchIfSpringAzureHasBeenRequested() {
OnRequestedSpringAzureDependencyCondition condition = new OnRequestedSpringAzureDependencyCondition();
MutableProjectDescription description = new MutableProjectDescription();
description.addDependency("azure-storage",
Dependency.withCoordinates("com.azure.spring", "spring-cloud-azure-starter-storage").build());
assertThat(matches(condition, description)).isTrue();
}

@Test
void shouldNotMatchIfSpringAzureHasNotBeenRequested() {
OnRequestedSpringAzureDependencyCondition condition = new OnRequestedSpringAzureDependencyCondition();
MutableProjectDescription description = new MutableProjectDescription();
description.addDependency("devtools",
Dependency.withCoordinates("org.springframework.boot", "spring-boot-devtools").build());
assertThat(matches(condition, description)).isFalse();
}

private static boolean matches(OnRequestedSpringAzureDependencyCondition condition,
ProjectDescription description) {
return condition.matches(description, mock(ConditionContext.class), mock(AnnotatedTypeMetadata.class));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2012-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.spring.start.site.extension.dependency.springazure;

import io.spring.initializr.web.project.ProjectRequest;
import io.spring.start.site.extension.AbstractExtensionTests;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link SpringAzureDockerComposeProjectGenerationConfiguration}.
*
* @author Eddú Meléndez
*/
class SpringAzureDockerComposeProjectGenerationConfigurationTests extends AbstractExtensionTests {

@Test
void springAzureDockerComposeDependencyIsAdded() {
ProjectRequest projectRequest = createProjectRequest("docker-compose", "azure-storage");
assertThat(mavenPom(projectRequest)).hasDependency("com.azure.spring", "spring-cloud-azure-docker-compose",
null, "test");
}

@Test
void shouldNotAddDockerComposeTestcontainersDependencyIfNoSpringAzureDependencyIsSelected() {
ProjectRequest projectRequest = createProjectRequest("docker-compose", "web");
assertThat(mavenPom(projectRequest)).doesNotHaveDependency("com.azure.spring",
"spring-cloud-azure-docker-compose");
}

}
Loading

0 comments on commit 5762b63

Please sign in to comment.