diff --git a/src/main/java/org/springframework/data/release/build/MavenArtifact.java b/src/main/java/org/springframework/data/release/build/MavenArtifact.java index 851c4ef..0811757 100644 --- a/src/main/java/org/springframework/data/release/build/MavenArtifact.java +++ b/src/main/java/org/springframework/data/release/build/MavenArtifact.java @@ -20,6 +20,7 @@ import static org.springframework.data.release.model.Projects.*; import lombok.EqualsAndHashCode; import lombok.Getter; +import org.springframework.data.release.model.ArtifactCoordinate; import org.springframework.data.release.model.ArtifactVersion; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.Project; @@ -77,4 +78,8 @@ public class MavenArtifact { return version.getNextDevelopmentVersion(); } + public ArtifactCoordinate toArtifactCoordinate() { + return ArtifactCoordinate.of(getGroupId(), getArtifactId()); + } + } diff --git a/src/main/java/org/springframework/data/release/deployment/AqlWriter.java b/src/main/java/org/springframework/data/release/deployment/AqlWriter.java new file mode 100644 index 0000000..ea4b034 --- /dev/null +++ b/src/main/java/org/springframework/data/release/deployment/AqlWriter.java @@ -0,0 +1,81 @@ +/* + * Copyright 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 + * + * http://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 org.springframework.data.release.deployment; + +import lombok.SneakyThrows; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import org.springframework.data.release.build.MavenArtifact; +import org.springframework.data.release.model.ArtifactCoordinate; +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.ModuleIteration; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @author Mark Paluch + */ +public class AqlWriter { + + private final DeploymentProperties.Authentication targetServer; + + private final ObjectMapper objectMapper; + + public AqlWriter(DeploymentProperties.Authentication targetServer, ObjectMapper objectMapper) { + this.targetServer = targetServer; + this.objectMapper = objectMapper; + } + + /** + * Create an AQL statement to find all associated artifacts with {@link ModuleIteration}. + * + * @param module + * @return + */ + @SneakyThrows + public String createFindAqlStatement(ModuleIteration module) { + + MavenArtifact mavenArtifact = new MavenArtifact(module); + Set>> matches = new LinkedHashSet<>(); + + matches.add(createAqlFilter(mavenArtifact.toArtifactCoordinate(), module)); + + module.getProject().doWithAdditionalArtifacts(artifactCoordinate -> { + matches.add(createAqlFilter(artifactCoordinate, module)); + }); + + Map repo = Collections.singletonMap("repo", targetServer.getTargetRepository()); + Map orMatches = Collections.singletonMap("$or", matches); + + return String.format("items.find(%s, %s)", objectMapper.writeValueAsString(repo), + objectMapper.writeValueAsString(orMatches)); + } + + private static Map> createAqlFilter(ArtifactCoordinate coordinate, + ModuleIteration module) { + + ArtifactVersion version = ArtifactVersion.of(module); + String groupIdPath = coordinate.getGroupId(); + String modulePath = String.format("%s/%s/%s", groupIdPath.replace('.', '/'), coordinate.getArtifactId(), version); + + return Collections.singletonMap("path", Collections.singletonMap("$match", modulePath)); + } + +} diff --git a/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java b/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java index 1ad9d6b..aaa901f 100644 --- a/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java +++ b/src/main/java/org/springframework/data/release/deployment/ArtifactoryClient.java @@ -20,14 +20,22 @@ import lombok.Value; import java.io.IOException; import java.net.URI; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.function.Consumer; import org.springframework.data.release.deployment.DeploymentProperties.Authentication; import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.SupportStatusAware; +import org.springframework.data.release.model.TrainIteration; import org.springframework.data.release.utils.Logger; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestOperations; import com.fasterxml.jackson.databind.ObjectMapper; @@ -41,6 +49,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; @RequiredArgsConstructor class ArtifactoryClient { + private final static String CREATE_RELEASE_BUNDLE_PATH = "/lifecycle/api/v2/release_bundle?project=spring"; + private final static String DISTRIBUTE_RELEASE_BUNDLE_PATH = "/lifecycle/api/v2/distribution/distribute/{releaseBundle}/{version}?project=spring"; + private final Logger logger; private final DeploymentProperties properties; private final RestOperations operations; @@ -74,10 +85,7 @@ class ArtifactoryClient { public void verify(SupportStatusAware status) { - URI verificationResource = properties - .getAuthentication(status) - .getServer() - .getVerificationResource(); + URI verificationResource = properties.getAuthentication(status).getServer().getVerificationResource(); try { @@ -110,10 +118,64 @@ class ArtifactoryClient { } public void deleteArtifacts(DeploymentInformation information) { - operations.delete(information.getDeleteBuildResource()); } + public void createRelease(String context, ArtifactoryReleaseBundle releaseBundle, + Authentication authentication) { + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + headers.add("X-JFrog-Signing-Key-Name", "packagesKey"); + HttpEntity entity = new HttpEntity<>(releaseBundle, headers); + + try { + ResponseEntity response = operations + .postForEntity(authentication.getServer().getUri() + CREATE_RELEASE_BUNDLE_PATH, entity, Map.class); + + if (!response.getStatusCode().is2xxSuccessful()) { + logger.warn(context, "Artifactory request failed: %d %s", response.getStatusCode().value(), + response.getBody()); + } else { + logger.log(context, "Artifactory request succeeded: %s %s", releaseBundle.getName(), + releaseBundle.getVersion()); + } + } catch (HttpStatusCodeException e) { + logger.warn(context, "Artifactory request failed: %d %s", e.getStatusCode().value(), + e.getResponseBodyAsString()); + } + } + + public void distributeRelease(TrainIteration train, String releaseName, String version, + Authentication authentication) { + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + String body = "{\n" + "\t\"auto_create_missing_repositories\": \"false\",\n" + "\t\"distribution_rules\": [\n" + + "\t\t{\n" + "\t\t\t\"site_name\": \"JP-SaaS\"\n" + "\t\t}\n" + "\t],\n" + "\t\"modifications\": {\n" + + "\t\t\"mappings\": [\n" + "\t\t\t{\n" + "\t\t\t\t\"input\": \"spring-enterprise-maven-prod-local/(.*)\",\n" + + "\t\t\t\t\"output\": \"spring-enterprise/$1\"\n" + "\t\t\t}\n" + "\t\t]\n" + "\t}\n" + "}"; + HttpEntity entity = new HttpEntity<>(body, headers); + + Map parameters = new LinkedHashMap<>(); + parameters.put("releaseBundle", releaseName); + parameters.put("version", version); + + try { + ResponseEntity response = operations + .postForEntity(authentication.getServer().getUri() + DISTRIBUTE_RELEASE_BUNDLE_PATH, entity, Map.class, + parameters); + + if (!response.getStatusCode().is2xxSuccessful()) { + logger.warn(train, "Artifactory request failed: %d %s", response.getStatusCode().value(), response.getBody()); + } else { + logger.log(train, "Artifactory request succeeded: %s %s", releaseName, version); + } + } catch (HttpStatusCodeException e) { + logger.warn(train, "Artifactory request failed: %d %s", e.getStatusCode().value(), e.getResponseBodyAsString()); + } + } + @Value static class PromotionRequest { String targetRepo, sourceRepo; diff --git a/src/main/java/org/springframework/data/release/deployment/ArtifactoryCommands.java b/src/main/java/org/springframework/data/release/deployment/ArtifactoryCommands.java index 36217a2..351ca82 100644 --- a/src/main/java/org/springframework/data/release/deployment/ArtifactoryCommands.java +++ b/src/main/java/org/springframework/data/release/deployment/ArtifactoryCommands.java @@ -17,13 +17,17 @@ package org.springframework.data.release.deployment; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import java.util.stream.Stream; import org.springframework.data.release.CliComponent; import org.springframework.data.release.TimedCommand; +import org.springframework.data.release.model.ModuleIteration; import org.springframework.data.release.model.SupportStatus; +import org.springframework.data.release.model.TrainIteration; import org.springframework.shell.core.annotation.CliCommand; +import org.springframework.shell.core.annotation.CliOption; /** * Commands to interact with Artifactory. @@ -35,11 +39,34 @@ import org.springframework.shell.core.annotation.CliCommand; class ArtifactoryCommands extends TimedCommand { private final @NonNull DeploymentOperations deployment; + private final @NonNull ArtifactoryOperations operations; @CliCommand(value = "artifactory verify", help = "Verifies authentication at Artifactory.") public void verify() { - Stream.of(SupportStatus.OSS, SupportStatus.COMMERCIAL) - .forEach(deployment::verifyAuthentication); + Stream.of(SupportStatus.OSS, SupportStatus.COMMERCIAL).forEach(deployment::verifyAuthentication); + } + + @CliCommand(value = "artifactory release create") + @SneakyThrows + public void createArtifactoryReleases(@CliOption(key = "", mandatory = true) TrainIteration trainIteration) { + + for (ModuleIteration moduleIteration : trainIteration) { + operations.createArtifactoryRelease(moduleIteration); + } + + // aggregator creation requires a bit of time + // otherwise we will see 16:19:04 "message" : "Release Bundle path not found: + // spring-release-bundles-v2/TNZ-spring-data-rest-commercial/4.0.15/release-bundle.json.evd" + Thread.sleep(2000); + + operations.createArtifactoryReleaseAggregator(trainIteration); + + } + + @CliCommand(value = "artifactory release distribute") + @SneakyThrows + public void distributeArtifactoryReleases(@CliOption(key = "", mandatory = true) TrainIteration trainIteration) { + operations.distributeArtifactoryReleaseAggregator(trainIteration); } } diff --git a/src/main/java/org/springframework/data/release/deployment/ArtifactoryOperations.java b/src/main/java/org/springframework/data/release/deployment/ArtifactoryOperations.java new file mode 100644 index 0000000..2cf4c2b --- /dev/null +++ b/src/main/java/org/springframework/data/release/deployment/ArtifactoryOperations.java @@ -0,0 +1,104 @@ +/* + * Copyright 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 + * + * http://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 org.springframework.data.release.deployment; + +import lombok.SneakyThrows; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +import org.springframework.data.release.model.ArtifactVersion; +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Projects; +import org.springframework.data.release.model.TrainIteration; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @author Mark Paluch + */ +@Component +class ArtifactoryOperations { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final AqlWriter aqlWriter; + private final DeploymentProperties.Authentication authentication; + private final ArtifactoryClient client; + + public ArtifactoryOperations(DeploymentProperties properties, ArtifactoryClient client) { + this.authentication = properties.getCommercial(); + this.aqlWriter = new AqlWriter(authentication, objectMapper); + this.client = client; + } + + @SneakyThrows + public void createArtifactoryRelease(ModuleIteration module) { + + ArtifactoryReleaseBundle releaseBundle = createReleaseBundle(module); + client.createRelease(module.getProject().getName(), releaseBundle, authentication); + } + + @SneakyThrows + public void createArtifactoryReleaseAggregator(TrainIteration train) { + + ModuleIteration bom = train.getModule(Projects.BOM); + String releaseName = "TNZ-spring-data-commercial-release"; + String version = ArtifactVersion.of(bom).toString(); + + List releaseBundles = train.stream().map(this::createReleaseBundleRef) + .collect(Collectors.toList()); + ArtifactoryReleaseBundle aggregator = new ArtifactoryReleaseBundle(releaseName, version, null, null, + "release_bundles", Collections.singletonMap("release_bundles", releaseBundles)); + + client.createRelease(train.toString(), aggregator, authentication); + } + + public void distributeArtifactoryReleaseAggregator(TrainIteration train) { + + ModuleIteration bom = train.getModule(Projects.BOM); + String releaseName = "TNZ-spring-data-commercial-release"; + String version = ArtifactVersion.of(bom).toString(); + + client.distributeRelease(train, releaseName, version, authentication); + } + + ArtifactoryReleaseBundle createReleaseBundle(ModuleIteration module) { + + String releaseName = getReleaseName(module); + String version = ArtifactVersion.of(module).toString(); + String aql = aqlWriter.createFindAqlStatement(module); + + return new ArtifactoryReleaseBundle(releaseName, version, null, null, "aql", Collections.singletonMap("aql", aql)); + } + + ArtifactoryReleaseBundle createReleaseBundleRef(ModuleIteration module) { + + String releaseName = getReleaseName(module); + String version = ArtifactVersion.of(module).toString(); + String aql = aqlWriter.createFindAqlStatement(module); + + return new ArtifactoryReleaseBundle(releaseName, version, "spring", "spring-release-bundles-v2", null, null); + } + + private static String getReleaseName(ModuleIteration module) { + String projectName = module.getProject().getName().toLowerCase(Locale.ROOT); + return String.format("TNZ-spring-data-%s-commercial", projectName); + } + +} diff --git a/src/main/java/org/springframework/data/release/deployment/ArtifactoryReleaseBundle.java b/src/main/java/org/springframework/data/release/deployment/ArtifactoryReleaseBundle.java new file mode 100644 index 0000000..75a9bf2 --- /dev/null +++ b/src/main/java/org/springframework/data/release/deployment/ArtifactoryReleaseBundle.java @@ -0,0 +1,41 @@ +/* + * Copyright 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 + * + * http://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 org.springframework.data.release.deployment; + +import lombok.Value; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author Mark Paluch + */ +@Value +@JsonInclude(value = JsonInclude.Include.NON_NULL) +class ArtifactoryReleaseBundle { + + @JsonProperty("release_bundle_name") String name; + + @JsonProperty("release_bundle_version") String version; + + @JsonProperty("project_key") String projectKey; + + @JsonProperty("repository_key") String repositoryKey; + + @JsonProperty("source_type") String source_type; + + @JsonProperty("source") Object source; +} diff --git a/src/main/java/org/springframework/data/release/model/Projects.java b/src/main/java/org/springframework/data/release/model/Projects.java index d0e33b5..12ebec7 100644 --- a/src/main/java/org/springframework/data/release/model/Projects.java +++ b/src/main/java/org/springframework/data/release/model/Projects.java @@ -53,19 +53,21 @@ public class Projects { BUILD = new Project("DATABUILD", "Build", Tracker.GITHUB) // .withAdditionalArtifacts(ArtifactCoordinates.forGroupId("org.springframework.data.build") - .artifacts("spring-data-build-parent", "spring-data-build-resources") + .artifacts("spring-data-build", "spring-data-parent", "spring-data-build-parent", + "spring-data-build-resources") .and(ArtifactCoordinate.of("org.springframework.data", "spring-data-releasetrain"))); COMMONS = new Project("DATACMNS", "Commons", Tracker.GITHUB).withDependencies(BUILD); JPA = new Project("DATAJPA", "JPA", Tracker.GITHUB) // .withDependencies(COMMONS) // - .withAdditionalArtifacts(ArtifactCoordinates.SPRING_DATA.artifacts("spring-data-envers")); + .withAdditionalArtifacts(ArtifactCoordinates.SPRING_DATA.artifacts("spring-data-envers", + "spring-data-jpa-parent", "spring-data-jpa-distribution")); MONGO_DB = new Project("DATAMONGO", "MongoDB", Tracker.GITHUB) // .withDependencies(COMMONS) // - .withAdditionalArtifacts( - ArtifactCoordinates.SPRING_DATA.artifacts("spring-data-mongodb-cross-store", "spring-data-mongodb-log4j")); + .withAdditionalArtifacts(ArtifactCoordinates.SPRING_DATA.artifacts("spring-data-mongodb-parent", + "spring-data-mongodb-distribution")); NEO4J = new Project("DATAGRAPH", "Neo4j", Tracker.GITHUB).withDependencies(COMMONS) .withMaintainer(ProjectMaintainer.COMMUNITY); @@ -79,7 +81,8 @@ public class Projects { CASSANDRA = new Project("DATACASS", "Cassandra", Tracker.GITHUB) // .withDependencies(COMMONS) // - .withAdditionalArtifacts(ArtifactCoordinates.SPRING_DATA.artifacts("spring-cql")) + .withAdditionalArtifacts(ArtifactCoordinates.SPRING_DATA.artifacts("spring-data-cassandra-parent", + "spring-data-cassandra-distribution")) .withFullName("Spring Data for Apache Cassandra"); ELASTICSEARCH = new Project("DATAES", "Elasticsearch", Tracker.GITHUB).withDependencies(COMMONS) @@ -89,13 +92,13 @@ public class Projects { REDIS = new Project("DATAREDIS", "Redis", Tracker.GITHUB).withDependencies(KEY_VALUE); - JDBC = new Project("DATAJDBC", "JDBC", Tracker.GITHUB) - .withAdditionalArtifacts( - ArtifactCoordinates.SPRING_DATA.artifacts("spring-data-relational", "spring-data-jdbc")) + JDBC = new Project("DATAJDBC", "JDBC", Tracker.GITHUB).withAdditionalArtifacts(ArtifactCoordinates.SPRING_DATA + .artifacts("spring-data-relational", "spring-data-relational-parent", "spring-data-jdbc")) .withDependencies(COMMONS); - RELATIONAL = new Project("DATAJDBC", "Relational", Tracker.GITHUB).withAdditionalArtifacts( - ArtifactCoordinates.SPRING_DATA.artifacts("spring-data-relational", "spring-data-jdbc", "spring-data-r2dbc")) + RELATIONAL = new Project("DATAJDBC", "Relational", Tracker.GITHUB) + .withAdditionalArtifacts(ArtifactCoordinates.SPRING_DATA.artifacts("spring-data-relational", + "spring-data-relational-parent", "spring-data-jdbc", "spring-data-jdbc-distribution", "spring-data-r2dbc")) .withDependencies(COMMONS); R2DBC = new Project("DATAR2DBC", "R2DBC", Tracker.GITHUB).withDependencies(COMMONS, JDBC); @@ -109,7 +112,8 @@ public class Projects { REST = new Project("DATAREST", "REST", Tracker.GITHUB) // .withDependencies(JPA, MONGO_DB, NEO4J, CASSANDRA, KEY_VALUE) // .withAdditionalArtifacts(ArtifactCoordinates.SPRING_DATA // - .artifacts("spring-data-rest-core", "spring-data-rest-core", "spring-data-rest-hal-browser", + .artifacts("spring-data-rest-parent", "spring-data-rest-core", "spring-data-rest-core", + "spring-data-rest-webmvc", "spring-data-rest-hal-browser", "spring-data-rest-distribution", "spring-data-rest-hal-explorer")); ENVERS = new Project("DATAENV", "Envers", Tracker.GITHUB).withDependencies(JPA); diff --git a/src/test/java/org/springframework/data/release/deployment/ArtifactoryOperationsUnitTests.java b/src/test/java/org/springframework/data/release/deployment/ArtifactoryOperationsUnitTests.java new file mode 100644 index 0000000..5afbe33 --- /dev/null +++ b/src/test/java/org/springframework/data/release/deployment/ArtifactoryOperationsUnitTests.java @@ -0,0 +1,114 @@ +/* + * Copyright 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 + * + * http://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 org.springframework.data.release.deployment; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.data.release.model.Iteration; +import org.springframework.data.release.model.ModuleIteration; +import org.springframework.data.release.model.Projects; +import org.springframework.data.release.model.ReleaseTrains; +import org.springframework.data.release.model.TrainIteration; + +/** + * Unit tests for {@link ArtifactoryOperations}. + * + * @author Mark Paluch + */ +class ArtifactoryOperationsUnitTests { + + DeploymentProperties properties = new DeploymentProperties(); + + ArtifactoryOperations operations; + + @BeforeEach + void setUp() { + + DeploymentProperties.Authentication authentication = new DeploymentProperties.Authentication(); + authentication.setProject("spring"); + authentication.setTargetRepository("spring-enterprise-maven-prod-local"); + + properties.setCommercial(authentication); + + operations = new ArtifactoryOperations(properties, mock(ArtifactoryClient.class)); + } + + @Test + void shouldCreateReleaseBundle() { + + TrainIteration iteration = ReleaseTrains.ULLMAN.getIteration(Iteration.SR1); + ModuleIteration module = iteration.getModule(Projects.COMMONS); + + ArtifactoryReleaseBundle releaseBundle = operations.createReleaseBundle(module); + + assertThat(releaseBundle.getName()).isEqualTo("TNZ-spring-data-commons-commercial"); + assertThat(releaseBundle.getVersion()).isEqualTo("3.1.1"); + assertThat(releaseBundle.getSource_type()).isEqualTo("aql"); + + assertThat(releaseBundle.getSource()).isInstanceOf(Map.class); + + String aql = ((Map) releaseBundle.getSource()).get("aql"); + assertThat(aql).contains( + "items.find({\"repo\":\"spring-enterprise-maven-prod-local\"}, {\"$or\":[{\"path\":{\"$match\":\"org/springframework/data/spring-data-commons/3.1.1\"}}]})"); + } + + @Test + void shouldCreateMultiModuleBundle() { + + TrainIteration iteration = ReleaseTrains.ULLMAN.getIteration(Iteration.SR1); + ModuleIteration module = iteration.getModule(Projects.RELATIONAL); + + ArtifactoryReleaseBundle releaseBundle = operations.createReleaseBundle(module); + + assertThat(releaseBundle.getName()).isEqualTo("TNZ-spring-data-relational-commercial"); + assertThat(releaseBundle.getVersion()).isEqualTo("3.1.1"); + assertThat(releaseBundle.getSource_type()).isEqualTo("aql"); + + assertThat(releaseBundle.getSource()).isInstanceOf(Map.class); + + String aql = ((Map) releaseBundle.getSource()).get("aql"); + assertThat(aql).contains("items.find({\"repo\":\"spring-enterprise-maven-prod-local\"}"); + assertThat(aql).contains("\"$match\":\"org/springframework/data/spring-data-relational-parent/3.1.1"); + assertThat(aql).contains("\"$match\":\"org/springframework/data/spring-data-jdbc/3.1.1"); + assertThat(aql).contains("\"$match\":\"org/springframework/data/spring-data-r2dbc/3.1.1"); + assertThat(aql).contains("\"$match\":\"org/springframework/data/spring-data-jdbc-distribution/3.1.1"); + } + + @Test + void shouldCreateBomBundle() { + + TrainIteration iteration = ReleaseTrains.ULLMAN.getIteration(Iteration.SR1); + ModuleIteration module = iteration.getModule(Projects.BOM); + + ArtifactoryReleaseBundle releaseBundle = operations.createReleaseBundle(module); + + assertThat(releaseBundle.getName()).isEqualTo("TNZ-spring-data-bom-commercial"); + assertThat(releaseBundle.getVersion()).isEqualTo("2023.0.1"); + assertThat(releaseBundle.getSource_type()).isEqualTo("aql"); + + assertThat(releaseBundle.getSource()).isInstanceOf(Map.class); + + String aql = ((Map) releaseBundle.getSource()).get("aql"); + assertThat(aql).contains( + "items.find({\"repo\":\"spring-enterprise-maven-prod-local\"}, {\"$or\":[{\"path\":{\"$match\":\"org/springframework/data/spring-data-bom/2023.0.1\"}}]})"); + } +}