From 9df85767c0e0cb37b3028b9a103349ed4bc3b5fb Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Fri, 3 Sep 2021 16:28:02 +0200 Subject: [PATCH] Include relevant upstream catalogs even if their versions aren't recommended for new projects Deprecated io.quarkus.maven.StreamCoords in favor of io.quarkus.registry.catalog.PlatformStreamCoords --- .../cli/common/TargetQuarkusVersionGroup.java | 8 +- .../registry/client/TestRegistryClient.java | 3 + .../client/TestRegistryClientBuilder.java | 107 +++++++++--- .../create/MultiplePlatformBomsTestBase.java | 21 ++- ...eferencingArchivedUpstreamVersionTest.java | 162 ++++++++++++++++++ .../java/io/quarkus/maven/StreamCoords.java | 19 ++ .../registry/ExtensionCatalogResolver.java | 158 +++++++++-------- .../registry/RegistryExtensionResolver.java | 8 + .../catalog/PlatformStreamCoords.java} | 10 +- .../catalog/json/JsonCatalogMerger.java | 46 +++++ .../catalog/json/JsonCatalogMergerTest.java | 99 +++++++++++ 11 files changed, 539 insertions(+), 102 deletions(-) create mode 100644 independent-projects/tools/devtools-testing/src/test/java/io/quarkus/devtools/project/create/QuarkusPlatformReferencingArchivedUpstreamVersionTest.java create mode 100644 independent-projects/tools/registry-client/src/main/java/io/quarkus/maven/StreamCoords.java rename independent-projects/tools/{artifact-api/src/main/java/io/quarkus/maven/StreamCoords.java => registry-client/src/main/java/io/quarkus/registry/catalog/PlatformStreamCoords.java} (71%) create mode 100644 independent-projects/tools/registry-client/src/test/java/io/quarkus/registry/catalog/json/JsonCatalogMergerTest.java diff --git a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java index 7444fd2418a984..11c55b6fe0a9b5 100644 --- a/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java +++ b/devtools/cli/src/main/java/io/quarkus/cli/common/TargetQuarkusVersionGroup.java @@ -2,15 +2,15 @@ import io.quarkus.cli.Version; import io.quarkus.maven.ArtifactCoords; -import io.quarkus.maven.StreamCoords; import io.quarkus.platform.tools.ToolsConstants; +import io.quarkus.registry.catalog.PlatformStreamCoords; import picocli.CommandLine; import picocli.CommandLine.Model.CommandSpec; public class TargetQuarkusVersionGroup { final static String FULL_EXAMPLE = ToolsConstants.DEFAULT_PLATFORM_BOM_GROUP_ID + ":" + ToolsConstants.DEFAULT_PLATFORM_BOM_ARTIFACT_ID + ":2.2.0.Final"; - StreamCoords streamCoords = null; + PlatformStreamCoords streamCoords = null; String validStream = null; ArtifactCoords platformBom = null; @@ -25,7 +25,7 @@ void setStream(String stream) { stream = stream.trim(); if (!stream.isEmpty()) { try { - streamCoords = StreamCoords.fromString(stream); + streamCoords = PlatformStreamCoords.fromString(stream); validStream = stream; } catch (IllegalArgumentException iex) { throw new CommandLine.ParameterException(spec.commandLine(), @@ -94,7 +94,7 @@ public boolean isStreamSpecified() { return streamCoords != null; } - public StreamCoords getStream() { + public PlatformStreamCoords getStream() { return streamCoords; } diff --git a/independent-projects/tools/devtools-testing/src/main/java/io/quarkus/devtools/testing/registry/client/TestRegistryClient.java b/independent-projects/tools/devtools-testing/src/main/java/io/quarkus/devtools/testing/registry/client/TestRegistryClient.java index 70f6a22777edd8..97f532d2a36387 100644 --- a/independent-projects/tools/devtools-testing/src/main/java/io/quarkus/devtools/testing/registry/client/TestRegistryClient.java +++ b/independent-projects/tools/devtools-testing/src/main/java/io/quarkus/devtools/testing/registry/client/TestRegistryClient.java @@ -120,6 +120,9 @@ private ExtensionCatalog deserializeExtensionCatalog(Path p) throws RegistryReso public PlatformCatalog resolvePlatforms(String quarkusVersion) throws RegistryResolutionException { final Path json = TestRegistryClientBuilder.getRegistryPlatformsCatalogPath(registryDir, quarkusVersion); log.debug("%s resolvePlatforms %s", config.getId(), json); + if (!Files.exists(json)) { + return null; + } try { return JsonCatalogMapperHelper.deserialize(json, JsonPlatformCatalog.class); } catch (IOException e) { diff --git a/independent-projects/tools/devtools-testing/src/main/java/io/quarkus/devtools/testing/registry/client/TestRegistryClientBuilder.java b/independent-projects/tools/devtools-testing/src/main/java/io/quarkus/devtools/testing/registry/client/TestRegistryClientBuilder.java index 74936a9ea295a0..195a7c1103ee43 100644 --- a/independent-projects/tools/devtools-testing/src/main/java/io/quarkus/devtools/testing/registry/client/TestRegistryClientBuilder.java +++ b/independent-projects/tools/devtools-testing/src/main/java/io/quarkus/devtools/testing/registry/client/TestRegistryClientBuilder.java @@ -106,6 +106,7 @@ public static class TestRegistryBuilder { private JsonRegistryQuarkusVersionsConfig quarkusVersions; private boolean external; private PlatformCatalog platformCatalog; + private JsonPlatformCatalog archivedPlatformCatalog; private List memberCatalogs; private List nonPlatformCatalogs; @@ -221,37 +222,72 @@ private void configure(Path registryDir) { platformConfig .setArtifact(new ArtifactCoords(registryGroupId, Constants.DEFAULT_REGISTRY_PLATFORMS_CATALOG_ARTIFACT_ID, null, "json", Constants.DEFAULT_REGISTRY_ARTIFACT_VERSION)); - if (platformCatalog == null) { + if (platformCatalog == null && archivedPlatformCatalog == null) { platformConfig.setDisabled(true); } else { final Path platformsDir = getRegistryPlatformsDir(registryDir); - persistPlatformCatalog(platformCatalog, platformsDir); final Map platformsByQuarkusVersion = new HashMap<>(); - for (Platform p : platformCatalog.getPlatforms()) { - for (PlatformStream s : p.getStreams()) { - for (PlatformRelease r : s.getReleases()) { - if (r.getQuarkusCoreVersion() == null) { - throw new IllegalStateException("Quarkus version has not be configured for platform release " - + p.getPlatformKey() + ":" + s.getId() + ":" + r.getVersion()); - } - final JsonPlatformCatalog c = platformsByQuarkusVersion.computeIfAbsent(r.getQuarkusCoreVersion(), - v -> new JsonPlatformCatalog()); - JsonPlatform platform = (JsonPlatform) c.getPlatform(p.getPlatformKey()); - if (platform == null) { - platform = new JsonPlatform(); - platform.setPlatformKey(p.getPlatformKey()); - c.addPlatform(platform); + if (platformCatalog != null) { + persistPlatformCatalog(platformCatalog, platformsDir); + for (Platform p : platformCatalog.getPlatforms()) { + for (PlatformStream s : p.getStreams()) { + for (PlatformRelease r : s.getReleases()) { + if (r.getQuarkusCoreVersion() == null) { + throw new IllegalStateException( + "Quarkus version has not be configured for platform release " + + p.getPlatformKey() + ":" + s.getId() + ":" + r.getVersion()); + } + final JsonPlatformCatalog c = platformsByQuarkusVersion.computeIfAbsent( + r.getQuarkusCoreVersion(), + v -> new JsonPlatformCatalog()); + JsonPlatform platform = (JsonPlatform) c.getPlatform(p.getPlatformKey()); + if (platform == null) { + platform = new JsonPlatform(); + platform.setPlatformKey(p.getPlatformKey()); + c.addPlatform(platform); + } + JsonPlatformStream stream = (JsonPlatformStream) platform.getStream(s.getId()); + if (stream == null) { + stream = new JsonPlatformStream(); + stream.setId(s.getId()); + platform.addStream(stream); + } + stream.addRelease(r); } - JsonPlatformStream stream = (JsonPlatformStream) platform.getStream(s.getId()); - if (stream == null) { - stream = new JsonPlatformStream(); - stream.setId(s.getId()); - platform.addStream(stream); + } + } + } + + if (archivedPlatformCatalog != null) { + for (Platform p : archivedPlatformCatalog.getPlatforms()) { + for (PlatformStream s : p.getStreams()) { + for (PlatformRelease r : s.getReleases()) { + if (r.getQuarkusCoreVersion() == null) { + throw new IllegalStateException( + "Quarkus version has not be configured for platform release " + + p.getPlatformKey() + ":" + s.getId() + ":" + r.getVersion()); + } + final JsonPlatformCatalog c = platformsByQuarkusVersion.computeIfAbsent( + r.getQuarkusCoreVersion(), + v -> new JsonPlatformCatalog()); + JsonPlatform platform = (JsonPlatform) c.getPlatform(p.getPlatformKey()); + if (platform == null) { + platform = new JsonPlatform(); + platform.setPlatformKey(p.getPlatformKey()); + c.addPlatform(platform); + } + JsonPlatformStream stream = (JsonPlatformStream) platform.getStream(s.getId()); + if (stream == null) { + stream = new JsonPlatformStream(); + stream.setId(s.getId()); + platform.addStream(stream); + } + stream.addRelease(r); } - stream.addRelease(r); } } } + for (Map.Entry entry : platformsByQuarkusVersion.entrySet()) { persistPlatformCatalog(entry.getValue(), platformsDir.resolve(entry.getKey())); } @@ -339,6 +375,33 @@ public TestPlatformCatalogReleaseBuilder newRelease(String version) { return new TestPlatformCatalogReleaseBuilder(this, release); } + public TestPlatformCatalogReleaseBuilder newArchivedRelease(String version) { + final JsonPlatformRelease release = new JsonPlatformRelease(); + release.setVersion(JsonPlatformReleaseVersion.fromString(version)); + + if (platform.registry.archivedPlatformCatalog == null) { + platform.registry.archivedPlatformCatalog = new JsonPlatformCatalog(); + } + + JsonPlatform archivedPlatform = (JsonPlatform) platform.registry.archivedPlatformCatalog + .getPlatform(platform.platform.getPlatformKey()); + if (archivedPlatform == null) { + archivedPlatform = new JsonPlatform(); + archivedPlatform.setPlatformKey(platform.platform.getPlatformKey()); + platform.registry.archivedPlatformCatalog.addPlatform(archivedPlatform); + } + + JsonPlatformStream archivedStream = (JsonPlatformStream) archivedPlatform.getStream(stream.getId()); + if (archivedStream == null) { + archivedStream = new JsonPlatformStream(); + archivedStream.setId(stream.getId()); + archivedPlatform.addStream(archivedStream); + } + + archivedStream.addRelease(release); + return new TestPlatformCatalogReleaseBuilder(this, release); + } + public TestPlatformCatalogPlatformBuilder platform() { return platform; } diff --git a/independent-projects/tools/devtools-testing/src/test/java/io/quarkus/devtools/project/create/MultiplePlatformBomsTestBase.java b/independent-projects/tools/devtools-testing/src/test/java/io/quarkus/devtools/project/create/MultiplePlatformBomsTestBase.java index d38ae9eb041363..59c58b2007a9dd 100644 --- a/independent-projects/tools/devtools-testing/src/test/java/io/quarkus/devtools/project/create/MultiplePlatformBomsTestBase.java +++ b/independent-projects/tools/devtools-testing/src/test/java/io/quarkus/devtools/project/create/MultiplePlatformBomsTestBase.java @@ -13,6 +13,8 @@ import io.quarkus.devtools.project.QuarkusProjectHelper; import io.quarkus.devtools.testing.registry.client.TestRegistryClientBuilder; import io.quarkus.maven.ArtifactCoords; +import io.quarkus.registry.RegistryResolutionException; +import io.quarkus.registry.catalog.PlatformStreamCoords; import io.quarkus.registry.config.RegistriesConfigLocator; import java.io.IOException; import java.nio.file.Files; @@ -89,7 +91,7 @@ protected QuarkusCommandOutcome addExtensions(Path projectDir, List exte protected QuarkusCommandOutcome createProject(Path projectDir, List extensions) throws Exception { - return createProject(projectDir, null, extensions); + return createProject(projectDir, (String) null, extensions); } protected QuarkusCommandOutcome createProject(Path projectDir, String quarkusVersion, List extensions) @@ -103,6 +105,17 @@ protected QuarkusCommandOutcome createProject(Path projectDir, String quarkusVer .execute(); } + protected QuarkusCommandOutcome createProject(Path projectDir, PlatformStreamCoords stream, List extensions) + throws Exception { + return new CreateProject( + getQuarkusProject(projectDir, stream)) + .groupId("org.acme") + .artifactId("acme-app") + .version("0.0.1-SNAPSHOT") + .extensions(new HashSet<>(extensions)) + .execute(); + } + protected List toPlatformExtensionCoords(String... artifactIds) { return toPlatformExtensionCoords(Arrays.asList(artifactIds)); } @@ -171,6 +184,12 @@ protected QuarkusProject getQuarkusProject(Path projectDir, String quarkusVersio return QuarkusProjectHelper.getProject(projectDir, BuildTool.MAVEN, quarkusVersion); } + protected QuarkusProject getQuarkusProject(Path projectDir, PlatformStreamCoords stream) + throws RegistryResolutionException { + return QuarkusProjectHelper.getProject(projectDir, + QuarkusProjectHelper.getCatalogResolver().resolveExtensionCatalog(stream), BuildTool.MAVEN); + } + static Path newProjectDir(String name) { Path projectDir = getProjectDir(name); if (Files.exists(projectDir)) { diff --git a/independent-projects/tools/devtools-testing/src/test/java/io/quarkus/devtools/project/create/QuarkusPlatformReferencingArchivedUpstreamVersionTest.java b/independent-projects/tools/devtools-testing/src/test/java/io/quarkus/devtools/project/create/QuarkusPlatformReferencingArchivedUpstreamVersionTest.java new file mode 100644 index 00000000000000..289b13db421308 --- /dev/null +++ b/independent-projects/tools/devtools-testing/src/test/java/io/quarkus/devtools/project/create/QuarkusPlatformReferencingArchivedUpstreamVersionTest.java @@ -0,0 +1,162 @@ +package io.quarkus.devtools.project.create; + +import io.quarkus.devtools.testing.registry.client.TestRegistryClientBuilder; +import io.quarkus.maven.ArtifactCoords; +import io.quarkus.registry.catalog.PlatformStreamCoords; +import java.nio.file.Path; +import java.util.Arrays; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class QuarkusPlatformReferencingArchivedUpstreamVersionTest extends MultiplePlatformBomsTestBase { + + private static final String DOWNSTREAM_PLATFORM_KEY = "io.downstream.platform"; + private static final String UPSTREAM_PLATFORM_KEY = "io.upstream.platform"; + + @BeforeAll + public static void setup() throws Exception { + TestRegistryClientBuilder.newInstance() + //.debug() + .baseDir(configDir()) + // registry + .newRegistry("downstream.registry.test") + .recognizedQuarkusVersions("*-downstream") + // platform key + .newPlatform(DOWNSTREAM_PLATFORM_KEY) + // 2.0 STREAM + .newStream("2.0") + // 2.0.4 release + .newRelease("2.0.4-downstream") + .quarkusVersion("2.2.2-downstream") + .upstreamQuarkusVersion("2.2.2") + // default bom including quarkus-core + essential metadata + .addCoreMember() + // foo platform member + .newMember("acme-foo-bom").addExtension("io.acme", "ext-a", "2.0.4-downstream").release().stream().platform() + .newStream("1.0") + // 1.0.4 release + .newRelease("1.0.4-downstream") + .quarkusVersion("1.1.1-downstream") + .upstreamQuarkusVersion("1.1.1") + // default bom including quarkus-core + essential metadata + .addCoreMember() + // foo platform member + .newMember("acme-foo-bom").addExtension("io.acme", "ext-a", "1.0.4-downstream").release() + .newMember("acme-e-bom").addExtension("io.acme", "ext-e", "1.0.4-downstream").release() + .stream().platform().registry() + .newNonPlatformCatalog("1.1.1-downstream").addExtension("io.acme", "ext-d", "4.0-downstream").registry() + .clientBuilder() + .newRegistry("upstream.registry.test") + // platform key + .newPlatform(UPSTREAM_PLATFORM_KEY) + // 2.0 STREAM + .newStream("2.0") + // 2.0.5 release + .newRelease("2.0.5") + .quarkusVersion("2.2.5") + // default bom including quarkus-core + essential metadata + .addCoreMember() + .newMember("acme-foo-bom").addExtension("io.acme", "ext-a", "2.0.5").release() + .newMember("acme-e-bom").addExtension("io.acme", "ext-e", "2.0.5").release() + .newMember("acme-bar-bom").addExtension("io.acme", "ext-b", "2.0.5").release().stream() + // 2.0.4 release + .newArchivedRelease("2.0.4") + .quarkusVersion("2.2.2") + // default bom including quarkus-core + essential metadata + .addCoreMember() + .newMember("acme-foo-bom").addExtension("io.acme", "ext-a", "2.0.4").release() + .newMember("acme-e-bom").addExtension("io.acme", "ext-e", "2.0.4").release() + .newMember("acme-bar-bom").addExtension("io.acme", "ext-b", "2.0.4").release().stream().platform() + // 1.0 STREAM + .newStream("1.0") + .newRelease("1.0.5") + .quarkusVersion("1.1.5") + // default bom including quarkus-core + essential metadata + .addCoreMember() + .newMember("acme-foo-bom").addExtension("io.acme", "ext-a", "1.0.5").addExtension("io.acme", "ext-e", "1.0.5") + .release() + .newMember("acme-bar-bom").addExtension("io.acme", "ext-b", "1.0.5").release() + .stream() + .newArchivedRelease("1.0.4") + .quarkusVersion("1.1.1") + // default bom including quarkus-core + essential metadata + .addCoreMember() + .newMember("acme-foo-bom").addExtension("io.acme", "ext-a", "1.0.4").addExtension("io.acme", "ext-e", "1.0.4") + .release() + .newMember("acme-bar-bom").addExtension("io.acme", "ext-b", "1.0.4").release() + .stream().platform().registry() + .newNonPlatformCatalog("2.2.2").addExtension("io.acme", "ext-c", "5.1").addExtension("io.acme", "ext-d", "6.0") + .registry() + .clientBuilder() + .build(); + + enableRegistryClient(); + } + + protected String getMainPlatformKey() { + return DOWNSTREAM_PLATFORM_KEY; + } + + @Test + public void addExtensionsFromAlreadyImportedPlatform() throws Exception { + final Path projectDir = newProjectDir("downstream-upstream-platform"); + createProject(projectDir, Arrays.asList("ext-a")); + + assertModel(projectDir, + toPlatformBomCoords("acme-foo-bom"), + Arrays.asList(new ArtifactCoords("io.acme", "ext-a", null)), + "2.0.4-downstream"); + + addExtensions(projectDir, Arrays.asList("ext-b", "ext-c", "ext-d", "ext-e")); + assertModel(projectDir, + Arrays.asList(mainPlatformBom(), platformMemberBomCoords("acme-foo-bom"), + new ArtifactCoords(UPSTREAM_PLATFORM_KEY, "acme-bar-bom", "pom", "2.0.4"), + new ArtifactCoords(UPSTREAM_PLATFORM_KEY, "acme-e-bom", "pom", "2.0.4")), + Arrays.asList(new ArtifactCoords("io.acme", "ext-a", null), + new ArtifactCoords("io.acme", "ext-b", null), + new ArtifactCoords("io.acme", "ext-e", null), + new ArtifactCoords("io.acme", "ext-c", "jar", "5.1"), + new ArtifactCoords("io.acme", "ext-d", "jar", "6.0")), + "2.0.4-downstream"); + } + + @Test + public void createWithExtensionsFromDifferentPlatforms() throws Exception { + final Path projectDir = newProjectDir("create-downstream-upstream-platform"); + createProject(projectDir, Arrays.asList("ext-a", "ext-b")); + + assertModel(projectDir, + Arrays.asList(mainPlatformBom(), platformMemberBomCoords("acme-foo-bom"), + new ArtifactCoords(UPSTREAM_PLATFORM_KEY, "acme-bar-bom", "pom", "2.0.4")), + Arrays.asList(new ArtifactCoords("io.acme", "ext-a", null), new ArtifactCoords("io.acme", "ext-b", null)), + "2.0.4-downstream"); + } + + @Test + public void createPreferringOlderStreamToNewerStreamCoveringLessExtensions() throws Exception { + final Path projectDir = newProjectDir("create-downstream-upstream-platform"); + createProject(projectDir, Arrays.asList("ext-a", "ext-b", "ext-e")); + + assertModel(projectDir, + Arrays.asList(mainPlatformBom(), platformMemberBomCoords("acme-foo-bom"), platformMemberBomCoords("acme-e-bom"), + new ArtifactCoords(UPSTREAM_PLATFORM_KEY, "acme-bar-bom", "pom", "1.0.4")), + Arrays.asList(new ArtifactCoords("io.acme", "ext-a", null), new ArtifactCoords("io.acme", "ext-b", null), + new ArtifactCoords("io.acme", "ext-e", null)), + "1.0.4-downstream"); + } + + @Test + public void createUsingStream2_0() throws Exception { + final Path projectDir = newProjectDir("created-using-downstream-stream"); + createProject(projectDir, new PlatformStreamCoords(DOWNSTREAM_PLATFORM_KEY, "2.0"), + Arrays.asList("ext-a", "ext-b", "ext-e")); + + assertModel(projectDir, + Arrays.asList(mainPlatformBom(), platformMemberBomCoords("acme-foo-bom"), + new ArtifactCoords(UPSTREAM_PLATFORM_KEY, "acme-e-bom", "pom", "2.0.4"), + new ArtifactCoords(UPSTREAM_PLATFORM_KEY, "acme-bar-bom", "pom", "2.0.4")), + Arrays.asList(new ArtifactCoords("io.acme", "ext-a", null), new ArtifactCoords("io.acme", "ext-b", null), + new ArtifactCoords("io.acme", "ext-e", null)), + "2.0.4-downstream"); + } +} diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/maven/StreamCoords.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/maven/StreamCoords.java new file mode 100644 index 00000000000000..599de0eb2276a9 --- /dev/null +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/maven/StreamCoords.java @@ -0,0 +1,19 @@ +package io.quarkus.maven; + +/** + * @deprecated in favor of {@link io.quarkus.registry.catalog.PlatformStreamCoords} + */ +@Deprecated +public class StreamCoords extends io.quarkus.registry.catalog.PlatformStreamCoords { + + public static StreamCoords fromString(String stream) { + final int colon = stream.indexOf(':'); + String platformKey = colon <= 0 ? null : stream.substring(0, colon); + String streamId = colon < 0 ? stream : stream.substring(colon + 1); + return new StreamCoords(platformKey, streamId); + } + + public StreamCoords(String platformKey, String streamId) { + super(platformKey, streamId); + } +} diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/ExtensionCatalogResolver.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/ExtensionCatalogResolver.java index 3d2059862f1467..ec04881f12ed5f 100644 --- a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/ExtensionCatalogResolver.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/ExtensionCatalogResolver.java @@ -4,12 +4,12 @@ import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver; import io.quarkus.devtools.messagewriter.MessageWriter; import io.quarkus.maven.ArtifactCoords; -import io.quarkus.maven.StreamCoords; import io.quarkus.registry.catalog.ExtensionCatalog; import io.quarkus.registry.catalog.Platform; import io.quarkus.registry.catalog.PlatformCatalog; import io.quarkus.registry.catalog.PlatformRelease; import io.quarkus.registry.catalog.PlatformStream; +import io.quarkus.registry.catalog.PlatformStreamCoords; import io.quarkus.registry.catalog.json.JsonCatalogMerger; import io.quarkus.registry.catalog.json.JsonExtensionCatalog; import io.quarkus.registry.catalog.json.JsonPlatform; @@ -291,6 +291,15 @@ public PlatformCatalog resolvePlatformCatalog(String quarkusVersion) throws Regi return result; } + private void collectPlatforms(PlatformCatalog catalog, List collectedPlatforms, + Set collectedPlatformKeys) { + for (Platform p : catalog.getPlatforms()) { + if (collectedPlatformKeys.add(p.getPlatformKey())) { + collectedPlatforms.add(p); + } + } + } + public PlatformCatalog resolvePlatformCatalogFromRegistry(String registryId) throws RegistryResolutionException { return registries.get(getRegistryIndex(registryId)).resolvePlatformCatalog(); } @@ -301,15 +310,6 @@ public PlatformCatalog resolvePlatformCatalogFromRegistry(String registryId, Str : registries.get(getRegistryIndex(registryId)).resolvePlatformCatalog(quarkusVersion); } - private void collectPlatforms(PlatformCatalog catalog, List collectedPlatforms, - Set collectedPlatformKeys) { - for (Platform p : catalog.getPlatforms()) { - if (collectedPlatformKeys.add(p.getPlatformKey())) { - collectedPlatforms.add(p); - } - } - } - private class ExtensionCatalogBuilder { private final List catalogs = new ArrayList<>(); final Map> registriesByQuarkusCore = new HashMap<>(); @@ -320,6 +320,12 @@ void addCatalog(ExtensionCatalog c) { catalogs.add(c); } + void addUpstreamQuarkusVersion(String quarkusVersion) { + if (!upstreamQuarkusVersions.contains(quarkusVersion)) { + upstreamQuarkusVersions.add(quarkusVersion); + } + } + List getRegistriesForQuarkusCore(String quarkusVersion) { return registriesByQuarkusCore.computeIfAbsent(quarkusVersion, v -> getRegistriesForQuarkusVersion(v)); } @@ -364,41 +370,71 @@ public ExtensionCatalog resolveExtensionCatalog() throws RegistryResolutionExcep final ExtensionCatalogBuilder catalogBuilder = new ExtensionCatalogBuilder(); for (int registryIndex = 0; registryIndex < registries.size(); ++registryIndex) { - RegistryExtensionResolver registry = registries.get(registryIndex); - final PlatformCatalog pc = registry.resolvePlatformCatalog(); + collectPlatformExtensions(catalogBuilder, registryIndex); + } + + return catalogBuilder.build(); + } + + private void collectPlatformExtensions(final ExtensionCatalogBuilder catalogBuilder, int registryIndex) + throws RegistryResolutionException { + final RegistryExtensionResolver registry = registries.get(registryIndex); + + final List downstreamPreferences = new ArrayList<>(catalogBuilder.upstreamQuarkusVersions.size()); + for (String quarkusVersion : catalogBuilder.upstreamQuarkusVersions) { + if (!registry.isAcceptsQuarkusVersionQueries(quarkusVersion)) { + continue; + } + final PlatformCatalog pc = registry.resolvePlatformCatalog(quarkusVersion); if (pc == null) { continue; } - int platformIndex = 0; - for (Platform platform : pc.getPlatforms()) { - platformIndex++; - for (PlatformStream stream : platform.getStreams()) { - int releaseIndex = 0; - for (PlatformRelease release : stream.getReleases()) { - releaseIndex++; - final int compatiblityCode = catalogBuilder - .getCompatibilityCode(release.getQuarkusCoreVersion(), - release.getUpstreamQuarkusCoreVersion()); - - int memberIndex = 0; - for (ArtifactCoords bom : release.getMemberBoms()) { - memberIndex++; - final ExtensionCatalog ec = registry.resolvePlatformExtensions(bom); - if (ec != null) { - final OriginPreference originPreference = new OriginPreference(registryIndex, platformIndex, - releaseIndex, memberIndex, compatiblityCode); - addOriginPreference(ec, originPreference); - catalogBuilder.addCatalog(ec); - } else { - log.warn("Failed to resolve extension catalog for %s from registry %s", bom, registry.getId()); - } + downstreamPreferences.add(pc); + } + + PlatformCatalog pc = registry.resolvePlatformCatalog(); + if (pc == null && downstreamPreferences.isEmpty()) { + return; + } + if (!downstreamPreferences.isEmpty()) { + downstreamPreferences.add(pc); + pc = JsonCatalogMerger.mergePlatformCatalogs(downstreamPreferences); + } + + int platformIndex = 0; + for (Platform platform : pc.getPlatforms()) { + platformIndex++; + for (PlatformStream stream : platform.getStreams()) { + int releaseIndex = 0; + for (PlatformRelease release : stream.getReleases()) { + releaseIndex++; + final String quarkusVersion = release.getQuarkusCoreVersion(); + final int compatiblityCode = catalogBuilder.getCompatibilityCode(quarkusVersion, + release.getUpstreamQuarkusCoreVersion()); + + if (!registry.isExclusiveProviderOf(quarkusVersion)) { + catalogBuilder.addUpstreamQuarkusVersion(quarkusVersion); + } + if (release.getUpstreamQuarkusCoreVersion() != null) { + catalogBuilder.addUpstreamQuarkusVersion(release.getUpstreamQuarkusCoreVersion()); + } + + int memberIndex = 0; + for (ArtifactCoords bom : release.getMemberBoms()) { + memberIndex++; + final ExtensionCatalog ec = registry.resolvePlatformExtensions(bom); + if (ec != null) { + final OriginPreference originPreference = new OriginPreference(registryIndex, platformIndex, + releaseIndex, memberIndex, compatiblityCode); + addOriginPreference(ec, originPreference); + catalogBuilder.addCatalog(ec); + } else { + log.warn("Failed to resolve extension catalog for %s from registry %s", bom, registry.getId()); } } } } } - - return catalogBuilder.build(); } private void addOriginPreference(final ExtensionCatalog ec, OriginPreference originPreference) { @@ -426,25 +462,22 @@ public ExtensionCatalog resolveExtensionCatalog(String quarkusCoreVersion) throw private ExtensionCatalog resolveExtensionCatalog(String quarkusCoreVersion, final ExtensionCatalogBuilder catalogBuilder, Set preferredPlatforms) throws RegistryResolutionException { - collectPlatforms(quarkusCoreVersion, catalogBuilder, preferredPlatforms); + collectPlatformExtensions(quarkusCoreVersion, catalogBuilder, preferredPlatforms); int i = 0; while (i < catalogBuilder.upstreamQuarkusVersions.size()) { - collectPlatforms(catalogBuilder.upstreamQuarkusVersions.get(i++), catalogBuilder, preferredPlatforms); + collectPlatformExtensions(catalogBuilder.upstreamQuarkusVersions.get(i++), catalogBuilder, preferredPlatforms); } return catalogBuilder.build(); } - public ExtensionCatalog resolveExtensionCatalog(StreamCoords streamCoords) throws RegistryResolutionException { + public ExtensionCatalog resolveExtensionCatalog(PlatformStreamCoords streamCoords) throws RegistryResolutionException { ensureRegistriesConfigured(); - final ExtensionCatalogBuilder catalogBuilder = new ExtensionCatalogBuilder(); - final String platformKey = streamCoords.getPlatformKey(); final String streamId = streamCoords.getStreamId(); PlatformStream stream = null; - RegistryExtensionResolver registry = null; int registryIndex = 0; while (registryIndex < registries.size()) { final RegistryExtensionResolver qer = registries.get(registryIndex++); @@ -456,7 +489,6 @@ public ExtensionCatalog resolveExtensionCatalog(StreamCoords streamCoords) throw for (Platform p : platforms.getPlatforms()) { stream = p.getStream(streamId); if (stream != null) { - registry = qer; break; } } @@ -466,7 +498,6 @@ public ExtensionCatalog resolveExtensionCatalog(StreamCoords streamCoords) throw continue; } stream = platform.getStream(streamId); - registry = qer; } break; } @@ -510,24 +541,12 @@ public ExtensionCatalog resolveExtensionCatalog(StreamCoords streamCoords) throw throw new RegistryResolutionException(buf.toString()); } - int releaseIndex = 0; + final List catalogs = new ArrayList<>(); for (PlatformRelease release : stream.getReleases()) { - final int compatiblityCode = catalogBuilder - .getCompatibilityCode(release.getQuarkusCoreVersion(), - release.getUpstreamQuarkusCoreVersion()); - ++releaseIndex; - int memberIndex = 0; - for (ArtifactCoords bom : release.getMemberBoms()) { - final ExtensionCatalog ec = registry.resolvePlatformExtensions(bom); - final OriginPreference originPreference = new OriginPreference(registryIndex, 1, - releaseIndex, ++memberIndex, compatiblityCode); - addOriginPreference(ec, originPreference); - - catalogBuilder.addCatalog(ec); - } + catalogs.add(resolveExtensionCatalog(release.getMemberBoms())); } - return catalogBuilder.build(); + return JsonCatalogMerger.merge(catalogs); } @SuppressWarnings("unchecked") @@ -623,7 +642,7 @@ public ExtensionCatalog resolveExtensionCatalog(Collection prefe release.setMemberBoms(coords); } - collectPlatforms(quarkusVersion, catalogBuilder, registry, platformIndex, + collectPlatformExtensions(quarkusVersion, catalogBuilder, registry, platformIndex, p); continue; } @@ -688,8 +707,8 @@ private int getRegistryIndex(String registryId) { throw new IllegalStateException(buf.toString()); } - private void collectPlatforms(String quarkusCoreVersion, ExtensionCatalogBuilder catalogBuilder, - Set preferredPlatforms) + private void collectPlatformExtensions(String quarkusCoreVersion, ExtensionCatalogBuilder catalogBuilder, + Set processedPlatforms) throws RegistryResolutionException { final List quarkusVersionRegistries = catalogBuilder .getRegistriesForQuarkusCore(quarkusCoreVersion); @@ -703,18 +722,18 @@ private void collectPlatforms(String quarkusCoreVersion, ExtensionCatalogBuilder if (platforms.isEmpty()) { continue; } - int platformIndex = preferredPlatforms.size(); + int platformIndex = processedPlatforms.size(); for (Platform p : platforms) { - if (preferredPlatforms.contains(p.getPlatformKey())) { + if (processedPlatforms.contains(p.getPlatformKey())) { continue; } ++platformIndex; - collectPlatforms(quarkusCoreVersion, catalogBuilder, registry, platformIndex, p); + collectPlatformExtensions(quarkusCoreVersion, catalogBuilder, registry, platformIndex, p); } } } - private void collectPlatforms(String quarkusCoreVersion, ExtensionCatalogBuilder catalogBuilder, + private void collectPlatformExtensions(String quarkusCoreVersion, ExtensionCatalogBuilder catalogBuilder, RegistryExtensionResolver registry, int platformIndex, Platform p) throws RegistryResolutionException { for (PlatformStream s : p.getStreams()) { @@ -738,9 +757,8 @@ private void collectPlatforms(String quarkusCoreVersion, ExtensionCatalogBuilder } final String upstreamQuarkusVersion = r.getUpstreamQuarkusCoreVersion(); - if (upstreamQuarkusVersion != null - && !catalogBuilder.upstreamQuarkusVersions.contains(upstreamQuarkusVersion)) { - catalogBuilder.upstreamQuarkusVersions.add(upstreamQuarkusVersion); + if (upstreamQuarkusVersion != null) { + catalogBuilder.addUpstreamQuarkusVersion(upstreamQuarkusVersion); } } } diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/RegistryExtensionResolver.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/RegistryExtensionResolver.java index 72eda74c8fcd98..0f203f1a5dc78b 100644 --- a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/RegistryExtensionResolver.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/RegistryExtensionResolver.java @@ -56,6 +56,14 @@ int checkQuarkusVersion(String quarkusVersion) { : VERSION_RECOGNIZED; } + boolean isExclusiveProviderOf(String quarkusVersion) { + return checkQuarkusVersion(quarkusVersion) == VERSION_EXCLUSIVE_PROVIDER; + } + + boolean isAcceptsQuarkusVersionQueries(String quarkusVersion) { + return checkQuarkusVersion(quarkusVersion) >= 0; + } + int checkPlatform(ArtifactCoords platform) { // TODO this should be allowed to check the full coordinates return checkQuarkusVersion(platform.getVersion()); diff --git a/independent-projects/tools/artifact-api/src/main/java/io/quarkus/maven/StreamCoords.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/catalog/PlatformStreamCoords.java similarity index 71% rename from independent-projects/tools/artifact-api/src/main/java/io/quarkus/maven/StreamCoords.java rename to independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/catalog/PlatformStreamCoords.java index 147ce225506010..8c8db274dbcca0 100644 --- a/independent-projects/tools/artifact-api/src/main/java/io/quarkus/maven/StreamCoords.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/catalog/PlatformStreamCoords.java @@ -1,17 +1,17 @@ -package io.quarkus.maven; +package io.quarkus.registry.catalog; -public class StreamCoords { +public class PlatformStreamCoords { final String platformKey; final String streamId; - public static StreamCoords fromString(String stream) { + public static PlatformStreamCoords fromString(String stream) { final int colon = stream.indexOf(':'); String platformKey = colon <= 0 ? null : stream.substring(0, colon); String streamId = colon < 0 ? stream : stream.substring(colon + 1); - return new StreamCoords(platformKey, streamId); + return new PlatformStreamCoords(platformKey, streamId); } - public StreamCoords(String platformKey, String streamId) { + public PlatformStreamCoords(String platformKey, String streamId) { this.platformKey = platformKey; this.streamId = streamId; } diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/catalog/json/JsonCatalogMerger.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/catalog/json/JsonCatalogMerger.java index 4f7b79ed2695b3..621565acb161b2 100644 --- a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/catalog/json/JsonCatalogMerger.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/catalog/json/JsonCatalogMerger.java @@ -5,6 +5,10 @@ import io.quarkus.registry.catalog.Extension; import io.quarkus.registry.catalog.ExtensionCatalog; import io.quarkus.registry.catalog.ExtensionOrigin; +import io.quarkus.registry.catalog.Platform; +import io.quarkus.registry.catalog.PlatformCatalog; +import io.quarkus.registry.catalog.PlatformRelease; +import io.quarkus.registry.catalog.PlatformStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -72,6 +76,48 @@ public static ExtensionCatalog merge(List catalogs) { return combined; } + public static PlatformCatalog mergePlatformCatalogs(List catalogs) { + if (catalogs.isEmpty()) { + throw new IllegalArgumentException("No catalogs provided"); + } + if (catalogs.size() == 1) { + return catalogs.get(0); + } + final JsonPlatformCatalog merged = new JsonPlatformCatalog(); + final Map platformMap = new HashMap<>(); + for (PlatformCatalog c : catalogs) { + for (Platform p : c.getPlatforms()) { + final JsonPlatform mergedPlatform = platformMap.computeIfAbsent(p.getPlatformKey(), k -> { + final JsonPlatform pl = new JsonPlatform(); + pl.setPlatformKey(p.getPlatformKey()); + merged.addPlatform(pl); + return pl; + }); + for (PlatformStream s : p.getStreams()) { + JsonPlatformStream mergedStream = (JsonPlatformStream) mergedPlatform.getStream(s.getId()); + if (mergedStream == null) { + mergedStream = new JsonPlatformStream(); + mergedStream.setId(s.getId()); + mergedPlatform.addStream(mergedStream); + } + for (PlatformRelease r : s.getReleases()) { + final PlatformRelease release = mergedStream.getRelease(r.getVersion()); + if (release == null) { + mergedStream.addRelease(r); + } + } + final Map mergedStreamMetadata = mergedStream.getMetadata(); + s.getMetadata().entrySet() + .forEach(entry -> mergedStreamMetadata.putIfAbsent(entry.getKey(), entry.getValue())); + } + p.getMetadata().entrySet() + .forEach(entry -> mergedPlatform.getMetadata().putIfAbsent(entry.getKey(), entry.getValue())); + } + c.getMetadata().entrySet().forEach(entry -> merged.getMetadata().putIfAbsent(entry.getKey(), entry.getValue())); + } + return merged; + } + private static List detectRoots(List catalogs) { final Set allDerivedFrom = new HashSet<>(catalogs.size()); for (ExtensionCatalog catalog : catalogs) { diff --git a/independent-projects/tools/registry-client/src/test/java/io/quarkus/registry/catalog/json/JsonCatalogMergerTest.java b/independent-projects/tools/registry-client/src/test/java/io/quarkus/registry/catalog/json/JsonCatalogMergerTest.java new file mode 100644 index 00000000000000..0026ad35ba9365 --- /dev/null +++ b/independent-projects/tools/registry-client/src/test/java/io/quarkus/registry/catalog/json/JsonCatalogMergerTest.java @@ -0,0 +1,99 @@ +package io.quarkus.registry.catalog.json; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.quarkus.maven.ArtifactCoords; +import io.quarkus.registry.catalog.Platform; +import io.quarkus.registry.catalog.PlatformCatalog; +import io.quarkus.registry.catalog.PlatformRelease; +import io.quarkus.registry.catalog.PlatformStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class JsonCatalogMergerTest { + + @Test + void testMergePlatformCatalogs() throws Exception { + + final List catalogs = new ArrayList<>(); + + JsonPlatformCatalog c = new JsonPlatformCatalog(); + catalogs.add(c); + JsonPlatform p = new JsonPlatform(); + c.addPlatform(p); + p.setPlatformKey("platform"); + JsonPlatformStream s = new JsonPlatformStream(); + s.setId("2.0"); + p.addStream(s); + JsonPlatformRelease r = new JsonPlatformRelease(); + r.setQuarkusCoreVersion("2.0.0"); + r.setVersion(JsonPlatformReleaseVersion.fromString("2.2.2")); + r.setMemberBoms(Collections.singletonList(ArtifactCoords.fromString("org.acme:acme-quarkus-bom::pom:2.2.2"))); + s.addRelease(r); + + s = new JsonPlatformStream(); + s.setId("1.0"); + p.addStream(s); + r = new JsonPlatformRelease(); + r.setQuarkusCoreVersion("1.0.1"); + r.setVersion(JsonPlatformReleaseVersion.fromString("1.1.2")); + r.setMemberBoms(Collections.singletonList(ArtifactCoords.fromString("org.acme:acme-quarkus-bom::pom:1.1.2"))); + s.addRelease(r); + + c = new JsonPlatformCatalog(); + catalogs.add(c); + p = new JsonPlatform(); + c.addPlatform(p); + p.setPlatformKey("platform"); + s = new JsonPlatformStream(); + s.setId("2.0"); + p.addStream(s); + r = new JsonPlatformRelease(); + r.setQuarkusCoreVersion("2.0.1"); + r.setVersion(JsonPlatformReleaseVersion.fromString("2.2.3")); + r.setMemberBoms(Collections.singletonList(ArtifactCoords.fromString("org.acme:acme-quarkus-bom::pom:2.2.3"))); + s.addRelease(r); + + s = new JsonPlatformStream(); + s.setId("1.0"); + p.addStream(s); + r = new JsonPlatformRelease(); + r.setQuarkusCoreVersion("1.0.0"); + r.setVersion(JsonPlatformReleaseVersion.fromString("1.1.1")); + r.setMemberBoms(Collections.singletonList(ArtifactCoords.fromString("org.acme:acme-quarkus-bom::pom:1.1.1"))); + s.addRelease(r); + + final PlatformCatalog merged = JsonCatalogMerger.mergePlatformCatalogs(catalogs); + Collection platforms = merged.getPlatforms(); + assertThat(platforms.size()).isEqualTo(1); + Platform platform = platforms.iterator().next(); + assertThat(platform.getPlatformKey()).isEqualTo("platform"); + assertThat(platform.getStreams().size()).isEqualTo(2); + Iterator streams = platform.getStreams().iterator(); + PlatformStream stream = streams.next(); + assertThat(stream.getId()).isEqualTo("2.0"); + assertThat(stream.getReleases().size()).isEqualTo(2); + Iterator releases = stream.getReleases().iterator(); + PlatformRelease release = releases.next(); + assertThat(release.getVersion()).isEqualTo(JsonPlatformReleaseVersion.fromString("2.2.2")); + assertThat(release.getQuarkusCoreVersion()).isEqualTo("2.0.0"); + release = releases.next(); + assertThat(release.getVersion()).isEqualTo(JsonPlatformReleaseVersion.fromString("2.2.3")); + assertThat(release.getQuarkusCoreVersion()).isEqualTo("2.0.1"); + + stream = streams.next(); + assertThat(stream.getId()).isEqualTo("1.0"); + assertThat(stream.getReleases().size()).isEqualTo(2); + releases = stream.getReleases().iterator(); + release = releases.next(); + assertThat(release.getVersion()).isEqualTo(JsonPlatformReleaseVersion.fromString("1.1.2")); + assertThat(release.getQuarkusCoreVersion()).isEqualTo("1.0.1"); + release = releases.next(); + assertThat(release.getVersion()).isEqualTo(JsonPlatformReleaseVersion.fromString("1.1.1")); + assertThat(release.getQuarkusCoreVersion()).isEqualTo("1.0.0"); + } +}