Skip to content

Commit fada305

Browse files
committed
Merge pull request #1484 from eddumelendez
* pr/1484-squash: Polish "Generate test apps for spring-ai projects" Generate test apps for spring-ai projects Closes gh-1484
2 parents 1f3d44a + f114449 commit fada305

26 files changed

+981
-2
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,43 @@
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 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+
* AI. A generated project may ultimately define a different set of dependencies according
32+
* to the contributors that have been executed. To contribute to the project according to
33+
* the real set, prefer querying the model itself rather than using this condition.
34+
*
35+
* @author Moritz Halbritter
36+
*/
37+
@Retention(RetentionPolicy.RUNTIME)
38+
@Target({ ElementType.TYPE, ElementType.METHOD })
39+
@Documented
40+
@Conditional(OnRequestedSpringAiDependencyCondition.class)
41+
@interface ConditionalOnRequestedSpringAiDependency {
42+
43+
}
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.springai;
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 ConditionalOnRequestedSpringAiDependency}.
29+
*
30+
* @author Moritz Halbritter
31+
*/
32+
class OnRequestedSpringAiDependencyCondition 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("org.springframework.ai")) {
39+
return true;
40+
}
41+
}
42+
return false;
43+
}
44+
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.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.ServiceConnection;
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 Chroma.
30+
*
31+
* @author Eddú Meléndez
32+
*/
33+
@ProjectGenerationConfiguration
34+
@ConditionalOnRequestedDependency("spring-ai-vectordb-chroma")
35+
class SpringAiChromaProjectGenerationConfiguration {
36+
37+
private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.chromadb.ChromaDBContainer";
38+
39+
@Bean
40+
@ConditionalOnRequestedDependency("testcontainers")
41+
ServiceConnectionsCustomizer chromaServiceConnectionsCustomizer(DockerServiceResolver serviceResolver) {
42+
return (serviceConnections) -> serviceResolver.doWith("chroma", (service) -> serviceConnections
43+
.addServiceConnection(ServiceConnection.ofContainer("chroma", service, TESTCONTAINERS_CLASS_NAME, false)));
44+
}
45+
46+
@Bean
47+
@ConditionalOnRequestedDependency("docker-compose")
48+
ComposeFileCustomizer chromaComposeFileCustomizer(DockerServiceResolver serviceResolver) {
49+
return (composeFile) -> serviceResolver.doWith("chroma",
50+
(service) -> composeFile.services().add("chroma", service));
51+
}
52+
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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.generator.version.VersionProperty;
27+
import io.spring.initializr.generator.version.VersionReference;
28+
import io.spring.initializr.metadata.InitializrMetadata;
29+
30+
import org.springframework.context.annotation.Bean;
31+
32+
/**
33+
* Configuration for generation of projects that depend on Spring AI Docker Compose.
34+
*
35+
* @author Eddú Meléndez
36+
*/
37+
@ProjectGenerationConfiguration
38+
@ConditionalOnRequestedDependency("docker-compose")
39+
@ConditionalOnRequestedSpringAiDependency
40+
public class SpringAiDockerComposeProjectGenerationConfiguration {
41+
42+
/**
43+
* Dependency id of
44+
* {@code org.springframework.ai:spring-ai-spring-boot-docker-compose}.
45+
*/
46+
public static final String DEPENDENCY_ID = "spring-ai-docker-compose";
47+
48+
@Bean
49+
BuildCustomizer<Build> springAiDockerComposeBuildCustomizer(InitializrMetadata metadata,
50+
ProjectDescription description) {
51+
// spring-ai-spring-boot-docker-compose is not managed in the BOM
52+
// See https://github.com/spring-projects/spring-ai/issues/1314
53+
VersionProperty springAiBomVersion = getSpringAiVersion(metadata, description);
54+
return (build) -> build.dependencies()
55+
.add(DEPENDENCY_ID,
56+
Dependency.withCoordinates("org.springframework.ai", "spring-ai-spring-boot-docker-compose")
57+
.version(VersionReference.ofProperty(springAiBomVersion))
58+
.scope(DependencyScope.RUNTIME));
59+
}
60+
61+
private static VersionProperty getSpringAiVersion(InitializrMetadata metadata, ProjectDescription description) {
62+
return metadata.getConfiguration().getEnv().getBoms().get("spring-ai").getVersionProperty();
63+
}
64+
65+
}
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.springai;
18+
19+
import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
20+
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
21+
import io.spring.start.site.container.DockerServiceResolver;
22+
import io.spring.start.site.container.ServiceConnections.ServiceConnection;
23+
import io.spring.start.site.container.ServiceConnectionsCustomizer;
24+
25+
import org.springframework.context.annotation.Bean;
26+
27+
/**
28+
* Configuration for generation of projects that depend on Milvus.
29+
*
30+
* @author Eddú Meléndez
31+
*/
32+
@ProjectGenerationConfiguration
33+
@ConditionalOnRequestedDependency("spring-ai-vectordb-milvus")
34+
class SpringAiMilvusProjectGenerationConfiguration {
35+
36+
private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.milvus.MilvusContainer";
37+
38+
@Bean
39+
@ConditionalOnRequestedDependency("testcontainers")
40+
ServiceConnectionsCustomizer milvusServiceConnectionsCustomizer(DockerServiceResolver serviceResolver) {
41+
return (serviceConnections) -> serviceResolver.doWith("milvus", (service) -> serviceConnections
42+
.addServiceConnection(ServiceConnection.ofContainer("milvus", service, TESTCONTAINERS_CLASS_NAME, false)));
43+
}
44+
45+
}

0 commit comments

Comments
 (0)