Skip to content

Commit

Permalink
Generate test apps for spring-ai projects
Browse files Browse the repository at this point in the history
Add support for Testcontainers and Docker Compose.

See gh-1484
  • Loading branch information
eddumelendez authored and mhalbritter committed Sep 5, 2024
1 parent 1f3d44a commit 25f4998
Show file tree
Hide file tree
Showing 23 changed files with 875 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,25 @@ public SimpleDockerServiceResolver() {
this.dockerServices.put("activeMQClassic", activeMQClassic());
this.dockerServices.put("artemis", artemis());
this.dockerServices.put("cassandra", cassandra());
this.dockerServices.put("chroma", chroma());
this.dockerServices.put("elasticsearch", elasticsearch());
this.dockerServices.put("kafka", kafka());
this.dockerServices.put("mariaDb", mariaDb());
this.dockerServices.put("milvus", milvus());
this.dockerServices.put("mongoDb", mongoDb());
this.dockerServices.put("mysql", mysql());
this.dockerServices.put("neo4j", neo4j());
this.dockerServices.put("ollama", ollama());
this.dockerServices.put("oracleFree", oracleFree());
this.dockerServices.put("pgvector", pgvector());
this.dockerServices.put("postgres", postgres());
this.dockerServices.put("pulsar", pulsar());
this.dockerServices.put("qdrant", qdrant());
this.dockerServices.put("rabbit", rabbit());
this.dockerServices.put("redis", redis());
this.dockerServices.put("redisStack", redisStack());
this.dockerServices.put("sqlServer", sqlServer());
this.dockerServices.put("weaviate", weaviate());
this.dockerServices.put("zipkin", zipkin());
}

Expand Down Expand Up @@ -83,6 +88,13 @@ private static DockerService cassandra() {
.build();
}

private static DockerService chroma() {
return DockerService.withImageAndTag("chromadb/chroma")
.website("https://hub.docker.com/r/chromadb/chroma")
.ports(8000)
.build();
}

private static DockerService elasticsearch() {
// They don't provide a 'latest' tag
return DockerService.withImageAndTag("docker.elastic.co/elasticsearch/elasticsearch:7.17.10")
Expand All @@ -102,6 +114,13 @@ private static DockerService mariaDb() {
return DockerService.withImageAndTag("mariadb").website("https://hub.docker.com/_/mariadb").ports(3306).build();
}

private static DockerService milvus() {
return DockerService.withImageAndTag("milvusdb/milvus")
.website("https://hub.docker.com/r/milvusdb/milvus")
.ports(19530)
.build();
}

private static DockerService mongoDb() {
return DockerService.withImageAndTag("mongo").website("https://hub.docker.com/_/mongo").ports(27017).build();
}
Expand All @@ -114,6 +133,13 @@ private static DockerService neo4j() {
return DockerService.withImageAndTag("neo4j").website("https://hub.docker.com/_/neo4j").ports(7687).build();
}

private static DockerService ollama() {
return DockerService.withImageAndTag("ollama/ollama")
.website("https://hub.docker.com/r/ollama/ollama")
.ports(11434)
.build();
}

private static DockerService oracleFree() {
return DockerService.withImageAndTag("gvenzl/oracle-free")
.website("https://hub.docker.com/r/gvenzl/oracle-free")
Expand Down Expand Up @@ -143,6 +169,13 @@ private static DockerService pulsar() {
.build();
}

private static DockerService qdrant() {
return DockerService.withImageAndTag("qdrant/qdrant")
.website("https://hub.docker.com/r/qdrant/qdrant")
.ports(6334)
.build();
}

private static DockerService rabbit() {
return DockerService.withImageAndTag("rabbitmq")
.website("https://hub.docker.com/_/rabbitmq")
Expand All @@ -168,6 +201,13 @@ private static DockerService sqlServer() {
.build();
}

private static DockerService weaviate() {
return DockerService.withImageAndTag("semitechnologies/weaviate")
.website("https://hub.docker.com/r/semitechnologies/weaviate")
.ports(8080)
.build();
}

private static DockerService zipkin() {
return DockerService.withImageAndTag("openzipkin/zipkin")
.website("https://hub.docker.com/r/openzipkin/zipkin/")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.springai;

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

import org.springframework.context.annotation.Bean;

/**
* Configuration for generation of projects that depend on Chroma.
*
* @author Eddú Meléndez
*/
@ProjectGenerationConfiguration
@ConditionalOnRequestedDependency("spring-ai-vectordb-chroma")
class SpringAiChromaProjectGenerationConfiguration {

private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.chromadb.ChromaDBContainer";

@Bean
@ConditionalOnRequestedDependency("testcontainers")
ServiceConnectionsCustomizer chromaServiceConnectionsCustomizer(InitializrMetadata metadata,
ProjectDescription description, DockerServiceResolver serviceResolver) {
return (serviceConnections) -> {
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
serviceResolver.doWith("chroma", (service) -> serviceConnections
.addServiceConnection(ServiceConnection.ofContainer("chroma", service, TESTCONTAINERS_CLASS_NAME)));
}
};
}

@Bean
@ConditionalOnRequestedDependency("docker-compose")
ComposeFileCustomizer chromaComposeFileCustomizer(InitializrMetadata metadata, ProjectDescription description,
DockerServiceResolver serviceResolver) {
return (composeFile) -> {
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
serviceResolver.doWith("chroma", (service) -> composeFile.services().add("chroma", service));
}
};
}

}
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.springai;

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.ProjectDescription;
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
import io.spring.initializr.generator.spring.build.BuildCustomizer;
import io.spring.initializr.metadata.InitializrMetadata;

import org.springframework.context.annotation.Bean;

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

@Bean
BuildCustomizer<Build> springAiDockerComposeBuildCustomizer(InitializrMetadata metadata,
ProjectDescription description) {
return (build) -> {
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
build.dependencies()
.add("spring-ai-docker-compose",
Dependency.withCoordinates("org.springframework.ai", "spring-ai-spring-boot-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.springai;

import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency;
import io.spring.initializr.generator.project.ProjectDescription;
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.start.site.container.DockerServiceResolver;
import io.spring.start.site.container.ServiceConnections.ServiceConnection;
import io.spring.start.site.container.ServiceConnectionsCustomizer;

import org.springframework.context.annotation.Bean;

/**
* Configuration for generation of projects that depend on Milvus.
*
* @author Eddú Meléndez
*/
@ProjectGenerationConfiguration
@ConditionalOnRequestedDependency("spring-ai-vectordb-milvus")
class SpringAiMilvusProjectGenerationConfiguration {

private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.milvus.MilvusContainer";

@Bean
@ConditionalOnRequestedDependency("testcontainers")
ServiceConnectionsCustomizer milvusServiceConnectionsCustomizer(InitializrMetadata metadata,
ProjectDescription description, DockerServiceResolver serviceResolver) {
return (serviceConnections) -> {
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
serviceResolver.doWith("milvus", (service) -> serviceConnections
.addServiceConnection(ServiceConnection.ofContainer("milvus", service, TESTCONTAINERS_CLASS_NAME)));
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.springai;

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

import org.springframework.context.annotation.Bean;

/**
* Configuration for generation of projects that depend on Ollama.
*
* @author Eddú Meléndez
*/
@ProjectGenerationConfiguration
@ConditionalOnRequestedDependency("spring-ai-ollama")
class SpringAiOllamaProjectGenerationConfiguration {

private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.ollama.OllamaContainer";

@Bean
@ConditionalOnRequestedDependency("testcontainers")
ServiceConnectionsCustomizer ollamaServiceConnectionsCustomizer(InitializrMetadata metadata,
ProjectDescription description, DockerServiceResolver serviceResolver) {
return (serviceConnections) -> {
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
serviceResolver.doWith("ollama", (service) -> serviceConnections
.addServiceConnection(ServiceConnection.ofContainer("ollama", service, TESTCONTAINERS_CLASS_NAME)));
}
};
}

@Bean
@ConditionalOnRequestedDependency("docker-compose")
ComposeFileCustomizer ollamaComposeFileCustomizer(InitializrMetadata metadata, ProjectDescription description,
DockerServiceResolver serviceResolver) {
return (composeFile) -> {
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
serviceResolver.doWith("ollama", (service) -> composeFile.services().add("ollama", service));
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.springai;

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

import org.springframework.context.annotation.Bean;

/**
* Configuration for generation of projects that depend on Qdrant.
*
* @author Eddú Meléndez
*/
@ProjectGenerationConfiguration
@ConditionalOnRequestedDependency("spring-ai-vectordb-qdrant")
class SpringAiQdrantProjectGenerationConfiguration {

private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.qdrant.QdrantContainer";

@Bean
@ConditionalOnRequestedDependency("testcontainers")
ServiceConnectionsCustomizer qdrantServiceConnectionsCustomizer(InitializrMetadata metadata,
ProjectDescription description, DockerServiceResolver serviceResolver) {
return (serviceConnections) -> {
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
serviceResolver.doWith("qdrant", (service) -> serviceConnections
.addServiceConnection(ServiceConnection.ofContainer("qdrant", service, TESTCONTAINERS_CLASS_NAME)));
}
};
}

@Bean
@ConditionalOnRequestedDependency("docker-compose")
ComposeFileCustomizer qdrantComposeFileCustomizer(InitializrMetadata metadata, ProjectDescription description,
DockerServiceResolver serviceResolver) {
return (composeFile) -> {
if (SpringAiVersion.version1OrLater(metadata, description.getPlatformVersion())) {
serviceResolver.doWith("qdrant", (service) -> composeFile.services().add("qdrant", service));
}
};
}

}
Loading

0 comments on commit 25f4998

Please sign in to comment.