Skip to content

Commit 25f4998

Browse files
eddumelendezmhalbritter
authored andcommitted
Generate test apps for spring-ai projects
Add support for Testcontainers and Docker Compose. See gh-1484
1 parent 1f3d44a commit 25f4998

23 files changed

+875
-0
lines changed

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

+40
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,25 @@ public SimpleDockerServiceResolver() {
3838
this.dockerServices.put("activeMQClassic", activeMQClassic());
3939
this.dockerServices.put("artemis", artemis());
4040
this.dockerServices.put("cassandra", cassandra());
41+
this.dockerServices.put("chroma", chroma());
4142
this.dockerServices.put("elasticsearch", elasticsearch());
4243
this.dockerServices.put("kafka", kafka());
4344
this.dockerServices.put("mariaDb", mariaDb());
45+
this.dockerServices.put("milvus", milvus());
4446
this.dockerServices.put("mongoDb", mongoDb());
4547
this.dockerServices.put("mysql", mysql());
4648
this.dockerServices.put("neo4j", neo4j());
49+
this.dockerServices.put("ollama", ollama());
4750
this.dockerServices.put("oracleFree", oracleFree());
4851
this.dockerServices.put("pgvector", pgvector());
4952
this.dockerServices.put("postgres", postgres());
5053
this.dockerServices.put("pulsar", pulsar());
54+
this.dockerServices.put("qdrant", qdrant());
5155
this.dockerServices.put("rabbit", rabbit());
5256
this.dockerServices.put("redis", redis());
5357
this.dockerServices.put("redisStack", redisStack());
5458
this.dockerServices.put("sqlServer", sqlServer());
59+
this.dockerServices.put("weaviate", weaviate());
5560
this.dockerServices.put("zipkin", zipkin());
5661
}
5762

@@ -83,6 +88,13 @@ private static DockerService cassandra() {
8388
.build();
8489
}
8590

91+
private static DockerService chroma() {
92+
return DockerService.withImageAndTag("chromadb/chroma")
93+
.website("https://hub.docker.com/r/chromadb/chroma")
94+
.ports(8000)
95+
.build();
96+
}
97+
8698
private static DockerService elasticsearch() {
8799
// They don't provide a 'latest' tag
88100
return DockerService.withImageAndTag("docker.elastic.co/elasticsearch/elasticsearch:7.17.10")
@@ -102,6 +114,13 @@ private static DockerService mariaDb() {
102114
return DockerService.withImageAndTag("mariadb").website("https://hub.docker.com/_/mariadb").ports(3306).build();
103115
}
104116

117+
private static DockerService milvus() {
118+
return DockerService.withImageAndTag("milvusdb/milvus")
119+
.website("https://hub.docker.com/r/milvusdb/milvus")
120+
.ports(19530)
121+
.build();
122+
}
123+
105124
private static DockerService mongoDb() {
106125
return DockerService.withImageAndTag("mongo").website("https://hub.docker.com/_/mongo").ports(27017).build();
107126
}
@@ -114,6 +133,13 @@ private static DockerService neo4j() {
114133
return DockerService.withImageAndTag("neo4j").website("https://hub.docker.com/_/neo4j").ports(7687).build();
115134
}
116135

136+
private static DockerService ollama() {
137+
return DockerService.withImageAndTag("ollama/ollama")
138+
.website("https://hub.docker.com/r/ollama/ollama")
139+
.ports(11434)
140+
.build();
141+
}
142+
117143
private static DockerService oracleFree() {
118144
return DockerService.withImageAndTag("gvenzl/oracle-free")
119145
.website("https://hub.docker.com/r/gvenzl/oracle-free")
@@ -143,6 +169,13 @@ private static DockerService pulsar() {
143169
.build();
144170
}
145171

172+
private static DockerService qdrant() {
173+
return DockerService.withImageAndTag("qdrant/qdrant")
174+
.website("https://hub.docker.com/r/qdrant/qdrant")
175+
.ports(6334)
176+
.build();
177+
}
178+
146179
private static DockerService rabbit() {
147180
return DockerService.withImageAndTag("rabbitmq")
148181
.website("https://hub.docker.com/_/rabbitmq")
@@ -168,6 +201,13 @@ private static DockerService sqlServer() {
168201
.build();
169202
}
170203

204+
private static DockerService weaviate() {
205+
return DockerService.withImageAndTag("semitechnologies/weaviate")
206+
.website("https://hub.docker.com/r/semitechnologies/weaviate")
207+
.ports(8080)
208+
.build();
209+
}
210+
171211
private static DockerService zipkin() {
172212
return DockerService.withImageAndTag("openzipkin/zipkin")
173213
.website("https://hub.docker.com/r/openzipkin/zipkin/")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.springai;
18+
19+
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
20+
import io.spring.initializr.generator.project.ProjectDescription;
21+
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
22+
import io.spring.initializr.metadata.InitializrMetadata;
23+
import io.spring.start.site.container.ComposeFileCustomizer;
24+
import io.spring.start.site.container.DockerServiceResolver;
25+
import io.spring.start.site.container.ServiceConnections.ServiceConnection;
26+
import io.spring.start.site.container.ServiceConnectionsCustomizer;
27+
28+
import org.springframework.context.annotation.Bean;
29+
30+
/**
31+
* Configuration for generation of projects that depend on Chroma.
32+
*
33+
* @author Eddú Meléndez
34+
*/
35+
@ProjectGenerationConfiguration
36+
@ConditionalOnRequestedDependency("spring-ai-vectordb-chroma")
37+
class SpringAiChromaProjectGenerationConfiguration {
38+
39+
private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.chromadb.ChromaDBContainer";
40+
41+
@Bean
42+
@ConditionalOnRequestedDependency("testcontainers")
43+
ServiceConnectionsCustomizer chromaServiceConnectionsCustomizer(InitializrMetadata metadata,
44+
ProjectDescription description, DockerServiceResolver serviceResolver) {
45+
return (serviceConnections) -> {
46+
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
47+
serviceResolver.doWith("chroma", (service) -> serviceConnections
48+
.addServiceConnection(ServiceConnection.ofContainer("chroma", service, TESTCONTAINERS_CLASS_NAME)));
49+
}
50+
};
51+
}
52+
53+
@Bean
54+
@ConditionalOnRequestedDependency("docker-compose")
55+
ComposeFileCustomizer chromaComposeFileCustomizer(InitializrMetadata metadata, ProjectDescription description,
56+
DockerServiceResolver serviceResolver) {
57+
return (composeFile) -> {
58+
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
59+
serviceResolver.doWith("chroma", (service) -> composeFile.services().add("chroma", service));
60+
}
61+
};
62+
}
63+
64+
}
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.springai;
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.ProjectDescription;
24+
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
25+
import io.spring.initializr.generator.spring.build.BuildCustomizer;
26+
import io.spring.initializr.metadata.InitializrMetadata;
27+
28+
import org.springframework.context.annotation.Bean;
29+
30+
/**
31+
* Configuration for generation of projects that depend on Spring AI Docker Compose.
32+
*
33+
* @author Eddú Meléndez
34+
*/
35+
@ProjectGenerationConfiguration
36+
@ConditionalOnRequestedDependency("docker-compose")
37+
class SpringAiDockerComposeProjectGenerationConfiguration {
38+
39+
@Bean
40+
BuildCustomizer<Build> springAiDockerComposeBuildCustomizer(InitializrMetadata metadata,
41+
ProjectDescription description) {
42+
return (build) -> {
43+
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
44+
build.dependencies()
45+
.add("spring-ai-docker-compose",
46+
Dependency.withCoordinates("org.springframework.ai", "spring-ai-spring-boot-docker-compose")
47+
.scope(DependencyScope.TEST_COMPILE));
48+
}
49+
};
50+
}
51+
52+
}
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.springai;
18+
19+
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
20+
import io.spring.initializr.generator.project.ProjectDescription;
21+
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
22+
import io.spring.initializr.metadata.InitializrMetadata;
23+
import io.spring.start.site.container.DockerServiceResolver;
24+
import io.spring.start.site.container.ServiceConnections.ServiceConnection;
25+
import io.spring.start.site.container.ServiceConnectionsCustomizer;
26+
27+
import org.springframework.context.annotation.Bean;
28+
29+
/**
30+
* Configuration for generation of projects that depend on Milvus.
31+
*
32+
* @author Eddú Meléndez
33+
*/
34+
@ProjectGenerationConfiguration
35+
@ConditionalOnRequestedDependency("spring-ai-vectordb-milvus")
36+
class SpringAiMilvusProjectGenerationConfiguration {
37+
38+
private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.milvus.MilvusContainer";
39+
40+
@Bean
41+
@ConditionalOnRequestedDependency("testcontainers")
42+
ServiceConnectionsCustomizer milvusServiceConnectionsCustomizer(InitializrMetadata metadata,
43+
ProjectDescription description, DockerServiceResolver serviceResolver) {
44+
return (serviceConnections) -> {
45+
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
46+
serviceResolver.doWith("milvus", (service) -> serviceConnections
47+
.addServiceConnection(ServiceConnection.ofContainer("milvus", service, TESTCONTAINERS_CLASS_NAME)));
48+
}
49+
};
50+
}
51+
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.springai;
18+
19+
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
20+
import io.spring.initializr.generator.project.ProjectDescription;
21+
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
22+
import io.spring.initializr.metadata.InitializrMetadata;
23+
import io.spring.start.site.container.ComposeFileCustomizer;
24+
import io.spring.start.site.container.DockerServiceResolver;
25+
import io.spring.start.site.container.ServiceConnections.ServiceConnection;
26+
import io.spring.start.site.container.ServiceConnectionsCustomizer;
27+
28+
import org.springframework.context.annotation.Bean;
29+
30+
/**
31+
* Configuration for generation of projects that depend on Ollama.
32+
*
33+
* @author Eddú Meléndez
34+
*/
35+
@ProjectGenerationConfiguration
36+
@ConditionalOnRequestedDependency("spring-ai-ollama")
37+
class SpringAiOllamaProjectGenerationConfiguration {
38+
39+
private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.ollama.OllamaContainer";
40+
41+
@Bean
42+
@ConditionalOnRequestedDependency("testcontainers")
43+
ServiceConnectionsCustomizer ollamaServiceConnectionsCustomizer(InitializrMetadata metadata,
44+
ProjectDescription description, DockerServiceResolver serviceResolver) {
45+
return (serviceConnections) -> {
46+
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
47+
serviceResolver.doWith("ollama", (service) -> serviceConnections
48+
.addServiceConnection(ServiceConnection.ofContainer("ollama", service, TESTCONTAINERS_CLASS_NAME)));
49+
}
50+
};
51+
}
52+
53+
@Bean
54+
@ConditionalOnRequestedDependency("docker-compose")
55+
ComposeFileCustomizer ollamaComposeFileCustomizer(InitializrMetadata metadata, ProjectDescription description,
56+
DockerServiceResolver serviceResolver) {
57+
return (composeFile) -> {
58+
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
59+
serviceResolver.doWith("ollama", (service) -> composeFile.services().add("ollama", service));
60+
}
61+
};
62+
}
63+
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.springai;
18+
19+
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
20+
import io.spring.initializr.generator.project.ProjectDescription;
21+
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
22+
import io.spring.initializr.metadata.InitializrMetadata;
23+
import io.spring.start.site.container.ComposeFileCustomizer;
24+
import io.spring.start.site.container.DockerServiceResolver;
25+
import io.spring.start.site.container.ServiceConnections.ServiceConnection;
26+
import io.spring.start.site.container.ServiceConnectionsCustomizer;
27+
28+
import org.springframework.context.annotation.Bean;
29+
30+
/**
31+
* Configuration for generation of projects that depend on Qdrant.
32+
*
33+
* @author Eddú Meléndez
34+
*/
35+
@ProjectGenerationConfiguration
36+
@ConditionalOnRequestedDependency("spring-ai-vectordb-qdrant")
37+
class SpringAiQdrantProjectGenerationConfiguration {
38+
39+
private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.qdrant.QdrantContainer";
40+
41+
@Bean
42+
@ConditionalOnRequestedDependency("testcontainers")
43+
ServiceConnectionsCustomizer qdrantServiceConnectionsCustomizer(InitializrMetadata metadata,
44+
ProjectDescription description, DockerServiceResolver serviceResolver) {
45+
return (serviceConnections) -> {
46+
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
47+
serviceResolver.doWith("qdrant", (service) -> serviceConnections
48+
.addServiceConnection(ServiceConnection.ofContainer("qdrant", service, TESTCONTAINERS_CLASS_NAME)));
49+
}
50+
};
51+
}
52+
53+
@Bean
54+
@ConditionalOnRequestedDependency("docker-compose")
55+
ComposeFileCustomizer qdrantComposeFileCustomizer(InitializrMetadata metadata, ProjectDescription description,
56+
DockerServiceResolver serviceResolver) {
57+
return (composeFile) -> {
58+
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
59+
serviceResolver.doWith("qdrant", (service) -> composeFile.services().add("qdrant", service));
60+
}
61+
};
62+
}
63+
64+
}

0 commit comments

Comments
 (0)