diff --git a/start-site/src/main/java/io/spring/start/site/container/SimpleDockerServiceResolver.java b/start-site/src/main/java/io/spring/start/site/container/SimpleDockerServiceResolver.java index 1cf7b764b65..268de1f56ec 100644 --- a/start-site/src/main/java/io/spring/start/site/container/SimpleDockerServiceResolver.java +++ b/start-site/src/main/java/io/spring/start/site/container/SimpleDockerServiceResolver.java @@ -46,6 +46,7 @@ public SimpleDockerServiceResolver() { this.dockerServices.put("neo4j", neo4j()); this.dockerServices.put("oracleFree", oracleFree()); this.dockerServices.put("oracleXe", oracleXe()); + this.dockerServices.put("pgvector", pgvector()); this.dockerServices.put("postgres", postgres()); this.dockerServices.put("pulsar", pulsar()); this.dockerServices.put("rabbit", rabbit()); @@ -127,6 +128,13 @@ private static DockerService oracleXe() { .build(); } + private static DockerService pgvector() { + return DockerService.withImageAndTag("pgvector/pgvector:pg16") + .website("https://hub.docker.com/r/pgvector/pgvector") + .ports(5432) + .build(); + } + private static DockerService postgres() { return DockerService.withImageAndTag("postgres") .website("https://hub.docker.com/_/postgres") diff --git a/start-site/src/main/java/io/spring/start/site/extension/dependency/postgresql/PgVectorProjectGenerationConfiguration.java b/start-site/src/main/java/io/spring/start/site/extension/dependency/postgresql/PgVectorProjectGenerationConfiguration.java new file mode 100644 index 00000000000..765c130f5b5 --- /dev/null +++ b/start-site/src/main/java/io/spring/start/site/extension/dependency/postgresql/PgVectorProjectGenerationConfiguration.java @@ -0,0 +1,84 @@ +/* + * 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.postgresql; + +import java.util.Map; + +import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency; +import io.spring.initializr.generator.project.ProjectDescription; +import io.spring.initializr.generator.version.Version; +import io.spring.initializr.generator.version.VersionParser; +import io.spring.initializr.generator.version.VersionRange; +import io.spring.initializr.versionresolver.MavenVersionResolver; +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; +import org.springframework.context.annotation.Configuration; + +/** + * Configuration for generation of projects that depend on PgVector. + * + * @author Eddú Meléndez + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnRequestedDependency("spring-ai-vectordb-pgvector") +class PgVectorProjectGenerationConfiguration { + + private static final String TESTCONTAINERS_CLASS_NAME = "org.testcontainers.containers.PostgreSQLContainer"; + + private static final VersionRange TESTCONTAINERS_1_19_7_OR_LATER = VersionParser.DEFAULT.parseRange("1.19.7"); + + private final MavenVersionResolver versionResolver; + + private final ProjectDescription description; + + PgVectorProjectGenerationConfiguration(MavenVersionResolver versionResolver, ProjectDescription description) { + this.versionResolver = versionResolver; + this.description = description; + } + + @Bean + @ConditionalOnRequestedDependency("testcontainers") + ServiceConnectionsCustomizer pgvectorServiceConnectionsCustomizer(DockerServiceResolver serviceResolver) { + Map resolve = this.versionResolver.resolveDependencies("org.springframework.boot", + "spring-boot-dependencies", this.description.getPlatformVersion().toString()); + String testcontainersVersion = resolve.get("org.testcontainers:testcontainers"); + + return (serviceConnections) -> { + if (TESTCONTAINERS_1_19_7_OR_LATER.match(Version.parse(testcontainersVersion))) { + serviceResolver.doWith("pgvector", (service) -> serviceConnections.addServiceConnection( + ServiceConnection.ofContainer("pgvector", service, TESTCONTAINERS_CLASS_NAME))); + } + }; + } + + @Bean + @ConditionalOnRequestedDependency("docker-compose") + ComposeFileCustomizer pgvectorComposeFileCustomizer(DockerServiceResolver serviceResolver) { + return (composeFile) -> serviceResolver.doWith("pgvector", + (service) -> composeFile.services() + .add("pgvector", + service.andThen((builder) -> builder.environment("POSTGRES_USER", "myuser") + .environment("POSTGRES_DB", "mydatabase") + .environment("POSTGRES_PASSWORD", "secret") + .label("org.springframework.boot.service-connection", "postgres")))); + } + +} diff --git a/start-site/src/main/java/io/spring/start/site/extension/dependency/testcontainers/TestcontainersModuleRegistry.java b/start-site/src/main/java/io/spring/start/site/extension/dependency/testcontainers/TestcontainersModuleRegistry.java index fc15fc822b2..61ee1705d5d 100644 --- a/start-site/src/main/java/io/spring/start/site/extension/dependency/testcontainers/TestcontainersModuleRegistry.java +++ b/start-site/src/main/java/io/spring/start/site/extension/dependency/testcontainers/TestcontainersModuleRegistry.java @@ -88,7 +88,7 @@ static Iterable create(Version platformVersion) { builders.add(onDependencies("oracle").customizeBuild(addModule("oracle-xe")) .customizeHelpDocument(addReferenceLink("Oracle-XE Module", "databases/oraclexe/"))); } - builders.add(onDependencies("postgresql").customizeBuild(addModule("postgresql")) + builders.add(onDependencies("postgresql", "spring-ai-vectordb-pgvector").customizeBuild(addModule("postgresql")) .customizeHelpDocument(addReferenceLink("Postgres Module", "databases/postgres/"))); builders.add(onDependencies("pulsar", "pulsar-reactive").customizeBuild(addModule("pulsar")) .customizeHelpDocument(addReferenceLink("Pulsar Module", "pulsar/"))); diff --git a/start-site/src/main/resources/META-INF/spring.factories b/start-site/src/main/resources/META-INF/spring.factories index 2d7dc6a490e..b02845bcb64 100644 --- a/start-site/src/main/resources/META-INF/spring.factories +++ b/start-site/src/main/resources/META-INF/spring.factories @@ -21,6 +21,7 @@ io.spring.start.site.extension.dependency.neo4j.Neo4jProjectGenerationConfigurat io.spring.start.site.extension.dependency.observability.ObservabilityProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.oracle.OracleProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.picocli.PicocliProjectGenerationConfiguration,\ +io.spring.start.site.extension.dependency.postgresql.PgVectorProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.postgresql.PostgresqlProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.redis.RedisProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.solace.SolaceProjectGenerationConfiguration,\ diff --git a/start-site/src/test/java/io/spring/start/site/extension/dependency/postgresql/PgVectorProjectGenerationConfigurationTests.java b/start-site/src/test/java/io/spring/start/site/extension/dependency/postgresql/PgVectorProjectGenerationConfigurationTests.java new file mode 100644 index 00000000000..24a596c62e5 --- /dev/null +++ b/start-site/src/test/java/io/spring/start/site/extension/dependency/postgresql/PgVectorProjectGenerationConfigurationTests.java @@ -0,0 +1,48 @@ +/* + * 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.postgresql; + +import io.spring.initializr.generator.test.project.ProjectStructure; +import io.spring.initializr.web.project.ProjectRequest; +import io.spring.start.site.extension.AbstractExtensionTests; +import org.junit.jupiter.api.Test; + +import org.springframework.core.io.ClassPathResource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link PgVectorProjectGenerationConfiguration}. + * + * @author Eddú Meléndez + */ +class PgVectorProjectGenerationConfigurationTests extends AbstractExtensionTests { + + @Test + void doesNothingWithoutDockerCompose() { + ProjectRequest request = createProjectRequest("web", "spring-ai-vectordb-pgvector"); + ProjectStructure structure = generateProject(request); + assertThat(structure.getProjectDirectory().resolve("compose.yaml")).doesNotExist(); + } + + @Test + void createsPostgresService() { + ProjectRequest request = createProjectRequest("docker-compose", "spring-ai-vectordb-pgvector"); + assertThat(composeFile(request)).hasSameContentAs(new ClassPathResource("compose/pgvector.yaml")); + } + +} diff --git a/start-site/src/test/java/io/spring/start/site/extension/dependency/testcontainers/TestcontainersProjectGenerationConfigurationTests.java b/start-site/src/test/java/io/spring/start/site/extension/dependency/testcontainers/TestcontainersProjectGenerationConfigurationTests.java index 815071fca46..42be1e38629 100644 --- a/start-site/src/test/java/io/spring/start/site/extension/dependency/testcontainers/TestcontainersProjectGenerationConfigurationTests.java +++ b/start-site/src/test/java/io/spring/start/site/extension/dependency/testcontainers/TestcontainersProjectGenerationConfigurationTests.java @@ -85,13 +85,15 @@ static Stream supportedEntriesBuild() { Arguments.arguments("data-couchbase-reactive", "couchbase"), Arguments.arguments("data-elasticsearch", "elasticsearch"), Arguments.arguments("data-mongodb", "mongodb"), Arguments.arguments("data-mongodb-reactive", "mongodb"), - Arguments.arguments("data-neo4j", "neo4j"), Arguments.arguments("spring-ai-vectordb-neo4j", "neo4j"), - Arguments.arguments("data-r2dbc", "r2dbc"), Arguments.arguments("db2", "db2"), - Arguments.arguments("kafka", "kafka"), Arguments.arguments("kafka-streams", "kafka"), - Arguments.arguments("mariadb", "mariadb"), Arguments.arguments("mysql", "mysql"), - Arguments.arguments("postgresql", "postgresql"), Arguments.arguments("oracle", "oracle-xe"), - Arguments.arguments("pulsar", "pulsar"), Arguments.arguments("pulsar-reactive", "pulsar"), - Arguments.arguments("solace", "solace"), Arguments.arguments("sqlserver", "mssqlserver")); + Arguments.arguments("data-neo4j", "neo4j"), Arguments.arguments("data-r2dbc", "r2dbc"), + Arguments.arguments("db2", "db2"), Arguments.arguments("kafka", "kafka"), + Arguments.arguments("kafka-streams", "kafka"), Arguments.arguments("mariadb", "mariadb"), + Arguments.arguments("mysql", "mysql"), Arguments.arguments("postgresql", "postgresql"), + Arguments.arguments("oracle", "oracle-xe"), Arguments.arguments("pulsar", "pulsar"), + Arguments.arguments("pulsar-reactive", "pulsar"), Arguments.arguments("solace", "solace"), + Arguments.arguments("spring-ai-vectordb-neo4j", "neo4j"), + Arguments.arguments("spring-ai-vectordb-pgvector", "postgresql"), + Arguments.arguments("sqlserver", "mssqlserver")); } static Stream supportedTestcontainersActiveMQEntriesBuild() { diff --git a/start-site/src/test/resources/compose/pgvector.yaml b/start-site/src/test/resources/compose/pgvector.yaml new file mode 100644 index 00000000000..36565c03d6c --- /dev/null +++ b/start-site/src/test/resources/compose/pgvector.yaml @@ -0,0 +1,11 @@ +services: + pgvector: + image: 'pgvector/pgvector:pg16' + environment: + - 'POSTGRES_DB=mydatabase' + - 'POSTGRES_PASSWORD=secret' + - 'POSTGRES_USER=myuser' + labels: + - "org.springframework.boot.service-connection=postgres" + ports: + - '5432'