Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#35] Support multiple version of a stream #55

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions core/src/main/java/org/wildfly/channel/Channel.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.wildfly.channel.spi.MavenVersionsResolver;
import org.wildfly.channel.version.VersionMatcher;
import org.wildfly.channel.version.VersionPatternMatcher;

/**
* Java representation of a Channel.
Expand Down Expand Up @@ -177,7 +179,7 @@ static class ResolveLatestVersionResult {
}


Optional<ResolveLatestVersionResult> resolveLatestVersion(String groupId, String artifactId, String extension, String classifier) {
Optional<ResolveLatestVersionResult> resolveLatestVersion(String groupId, String artifactId, String extension, String classifier, String baseVersion) {
requireNonNull(groupId);
requireNonNull(artifactId);
requireNonNull(resolver);
Expand All @@ -190,7 +192,7 @@ Optional<ResolveLatestVersionResult> resolveLatestVersion(String groupId, String
// we return the latest value from the required channels
Map<String, Channel> foundVersions = new HashMap<>();
for (Channel requiredChannel : requiredChannels) {
Optional<Channel.ResolveLatestVersionResult> found = requiredChannel.resolveLatestVersion(groupId, artifactId, extension, classifier);
Optional<Channel.ResolveLatestVersionResult> found = requiredChannel.resolveLatestVersion(groupId, artifactId, extension, classifier, baseVersion);
if (found.isPresent()) {
foundVersions.put(found.get().version, found.get().channel);
}
Expand All @@ -211,6 +213,18 @@ Optional<ResolveLatestVersionResult> resolveLatestVersion(String groupId, String
// if there is a version pattern, we resolve all versions from Maven to find the latest one
Set<String> versions = resolver.getAllVersions(groupId, artifactId, extension, classifier);
foundVersion = foundStream.get().getVersionComparator().matches(versions);
} else if (stream.getVersions() != null) {
if (baseVersion == null) {
throw new RuntimeException(String.format("Unable to resolve the latest version for the stream %s:%s. Multiple versions are specified and baseVersion is null",
groupId, artifactId));
}
Map<String, Pattern> versionStreams = stream.getVersions();
for (Map.Entry<String, Pattern> entry : versionStreams.entrySet()) {
if (Pattern.compile(entry.getKey()).matcher(baseVersion).matches()) {
Set<String> versions = resolver.getAllVersions(groupId, artifactId, extension, classifier);
foundVersion = new VersionPatternMatcher(entry.getValue()).matches(versions);
}
}
}

if (foundVersion.isPresent()) {
Expand Down
29 changes: 25 additions & 4 deletions core/src/main/java/org/wildfly/channel/ChannelRecorder.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@
*/
package org.wildfly.channel;

import static java.util.regex.Pattern.compile;
import static java.util.regex.Pattern.quote;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;

class ChannelRecorder {

Expand All @@ -26,10 +33,24 @@ class ChannelRecorder {
null,
Collections.emptyList());

void recordStream(String groupId, String artifactId, String version) {
boolean isRecorded = recordedChannel.getStreams().stream().anyMatch(s -> s.getGroupId().equals(groupId) && s.getArtifactId().equals(artifactId) && s.getVersion().equals(version));
if (!isRecorded) {
recordedChannel.addStream(new Stream(groupId, artifactId, version, null));
void recordStream(String groupId, String artifactId, String newVersion) {
Optional<Stream> optStream = recordedChannel.getStreams().stream().filter(s -> s.getGroupId().equals(groupId) && s.getArtifactId().equals(artifactId)).findFirst();
if (!optStream.isPresent()) {
recordedChannel.addStream(new Stream(groupId, artifactId, newVersion, null, null));
} else {
Stream stream = optStream.get();
String version = stream.getVersion();
Map<String, Pattern> versions = stream.getVersions();
if (version != null) {
if (version.equals(newVersion)) {
return;
}
versions = new HashMap<>();
versions.put(quote(version), compile(quote(version)));
}
versions.put(quote(newVersion), compile(quote(newVersion)));
recordedChannel.getStreams().remove(stream);
recordedChannel.addStream(new Stream(groupId, artifactId, null, null, versions));
}
}

Expand Down
20 changes: 14 additions & 6 deletions core/src/main/java/org/wildfly/channel/ChannelSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.io.File;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import org.wildfly.channel.spi.MavenVersionsResolver;
Expand Down Expand Up @@ -55,11 +56,17 @@ public ChannelSession(List<Channel> channels, MavenVersionsResolver.Factory fact
* @param artifactId - required
* @param extension - can be null
* @param classifier - can be null
* @param baseVersion - can be null. The base version is required when the stream for the component specifies multiple versions and requires the base version to
* determine the appropriate version to resolve.
* @return the Maven Artifact (with a file corresponding to the artifact).
* @throws UnresolvedMavenArtifactException if the latest version can not be resolved or the artifact itself can not be resolved
*/
public MavenArtifact resolveMavenArtifact(String groupId, String artifactId, String extension, String classifier) throws UnresolvedMavenArtifactException {
Channel.ResolveLatestVersionResult result = findChannelWithLatestVersion(groupId, artifactId, extension, classifier);
public MavenArtifact resolveMavenArtifact(String groupId, String artifactId, String extension, String classifier, String baseVersion) throws UnresolvedMavenArtifactException {
requireNonNull(groupId);
requireNonNull(artifactId);
// baseVersion is not used at the moment but will provide essential to support advanced use cases to determine multiple streams of the same Maven component.

Channel.ResolveLatestVersionResult result = findChannelWithLatestVersion(groupId, artifactId, extension, classifier, baseVersion);
String latestVersion = result.version;
Channel channel = result.channel;

Expand Down Expand Up @@ -98,11 +105,12 @@ public MavenArtifact resolveDirectMavenArtifact(String groupId, String artifactI
* @param artifactId - required
* @param extension - can be null
* @param classifier - can be null
* @param baseVersion - required
* @return the latest version if a Maven artifact
* @throws UnresolvedMavenArtifactException if the latest version cannot be established
*/
public String findLatestMavenArtifactVersion(String groupId, String artifactId, String extension, String classifier) throws UnresolvedMavenArtifactException {
return findChannelWithLatestVersion(groupId, artifactId, extension, classifier).version;
public String findLatestMavenArtifactVersion(String groupId, String artifactId, String extension, String classifier, String baseVersion) throws UnresolvedMavenArtifactException {
return findChannelWithLatestVersion(groupId, artifactId, extension, classifier, baseVersion).version;
}

@Override
Expand All @@ -125,12 +133,12 @@ public Channel getRecordedChannel() {
return recorder.getRecordedChannel();
}

private Channel.ResolveLatestVersionResult findChannelWithLatestVersion(String groupId, String artifactId, String extension, String classifier) throws UnresolvedMavenArtifactException {
private Channel.ResolveLatestVersionResult findChannelWithLatestVersion(String groupId, String artifactId, String extension, String classifier, String baseVersion) throws UnresolvedMavenArtifactException {
requireNonNull(groupId);
requireNonNull(artifactId);

for (Channel channel : channels) {
Optional<Channel.ResolveLatestVersionResult> result = channel.resolveLatestVersion(groupId, artifactId, extension, classifier);
Optional<Channel.ResolveLatestVersionResult> result = channel.resolveLatestVersion(groupId, artifactId, extension, classifier, baseVersion);
if (result.isPresent()) {
return result.get();
}
Expand Down
35 changes: 29 additions & 6 deletions core/src/main/java/org/wildfly/channel/Stream.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
import static java.util.Objects.requireNonNull;

import java.util.Map;
import java.util.regex.Pattern;

import com.fasterxml.jackson.annotation.JsonCreator;
Expand Down Expand Up @@ -61,28 +62,41 @@ public class Stream implements Comparable<Stream> {
*/
private final Pattern versionPattern;

/**
* Multiple versions stream
* The key is a regular expression to express the baseline version.
* The value is a regular expression that matches the latest version of that baseline.
*
* Only one of {@code version}, {@code versionPattern}, {@code versions} must be set.
*/
private final Map<String, Pattern> versions;

private VersionMatcher versionMatcher;

@JsonCreator
public Stream(@JsonProperty(value = "groupId", required = true) String groupId,
@JsonProperty(value = "artifactId", required = true) String artifactId,
@JsonProperty("version") String version,
@JsonProperty("versionPattern") Pattern versionPattern) {
@JsonProperty("versionPattern") Pattern versionPattern,
@JsonProperty("versions") Map<String, Pattern> versions) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.versionPattern = versionPattern;
this.versions = versions;
validate();
initVersionMatcher();
}

private void initVersionMatcher() {
if (version != null) {
versionMatcher = new FixedVersionMatcher(version);
} else {
} else if (versionPattern != null ){
requireNonNull(versionPattern);
// let's instead find a version matching the pattern
versionMatcher = new VersionPatternMatcher(versionPattern);
} else {
//TODO
}
}

Expand All @@ -92,10 +106,13 @@ private void validate() {
String.format("Invalid stream. the groupId does not accept wildcard '*'"));
}

if ((version != null && versionPattern != null) ||
(version == null && versionPattern == null )) {
if ((version == null && versionPattern == null && versions == null) ||
(version != null && (versionPattern != null || versions != null)) ||
(versionPattern != null && (version != null || versions != null)) ||
(versions != null && (version != null || versionPattern != null))
) {
throw new IllegalArgumentException(
String.format("Invalid stream. Only one of version, versionPattern field must be set"));
String.format("Invalid stream. Only one of version, versionPattern, versionStreams field must be set"));
}
}

Expand All @@ -117,6 +134,11 @@ public Pattern getVersionPattern() {
return versionPattern;
}

@JsonInclude(NON_NULL)
public Map<String, Pattern> getVersions() {
return versions;
}

@JsonIgnore
public VersionMatcher getVersionComparator() {
return versionMatcher;
Expand All @@ -128,7 +150,8 @@ public String toString() {
"groupId='" + groupId + '\'' +
", artifactId='" + artifactId + '\'' +
", version='" + version + '\'' +
", versionPattern=" + versionPattern +
", versionPattern=" + versionPattern + '\'' +
", versions=" + versions +
", versionComparator=" + versionMatcher +
'}';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
"versionPattern" : {
"description": "VersionPattern of the stream. This is a regular expression that matches any version from this stream. Only one of version, versionPattern must be set.",
"type": "string"
},
"versionStreams" : {
"description": "Multiple versions of the stream. The key are regular expressions that matches the baseline version, the value are regular expresssion to determine the latest versio for that baseline",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo
s/versio /version /

"type": "object",
"additionalProperties": { "type": "string" }
}
},
"required": ["groupId", "artifactId"],
Expand All @@ -85,7 +90,11 @@
},
{
"required": ["versionPattern"]
},
{
"required": ["versions"]
}

]
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
*/
package org.wildfly.channel;

import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
Expand All @@ -28,10 +30,13 @@

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -75,12 +80,12 @@ public void testChannelRecorder() throws IOException, UnresolvedMavenArtifactExc
.thenReturn(mock(File.class));

try (ChannelSession session = new ChannelSession(channels, factory)) {
session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null);
session.resolveMavenArtifact("org.wildfly.core", "wildfly.core.cli", null, null);
session.resolveMavenArtifact("io.undertow", "undertow-core", null, null);
session.resolveMavenArtifact("io.undertow", "undertow-servlet", null, null);
session.resolveMavenArtifact("org.wildfly", "wildfly-ee-galleon-pack", null, null, "20.0.0.Final");
session.resolveMavenArtifact("org.wildfly.core", "wildfly.core.cli", null, null, "15.0.0.Final");
session.resolveMavenArtifact("io.undertow", "undertow-core", null, null, "1.0.0.Final");
session.resolveMavenArtifact("io.undertow", "undertow-servlet", null, null, "1.0.0.Final");
// This should not be recorded, size should remain 4.
session.resolveMavenArtifact("io.undertow", "undertow-servlet", null, null);
session.resolveMavenArtifact("io.undertow", "undertow-servlet", null, null, "1.0.0.Final");

Channel recordedChannel = session.getRecordedChannel();
System.out.println(ChannelMapper.toYaml(recordedChannel));
Expand Down Expand Up @@ -121,6 +126,57 @@ public void testChannelRecorder() throws IOException, UnresolvedMavenArtifactExc
}
}

@Test
public void testChannelRecorderWithMultipleVersions() throws IOException, UnresolvedMavenArtifactException {
MavenVersionsResolver.Factory factory = mock(MavenVersionsResolver.Factory.class);
MavenVersionsResolver resolver = mock(MavenVersionsResolver.class);

when(factory.create())
.thenReturn(resolver);
when(resolver.getAllVersions(eq("io.undertow"), anyString(), eq(null), eq(null)))
.thenReturn(Set.of("1.0.0.Final", "1.1.1.Final", "2.0.0.Final", "2.1.0.Final", "2.2.0.Final"));
when(resolver.resolveArtifact(anyString(), anyString(), eq(null), eq(null), anyString()))
.thenReturn(mock(File.class));

try (ChannelSession session = new ChannelSession(emptyList(), factory)) {
session.resolveDirectMavenArtifact("io.undertow", "undertow-core", null, null, "1.0.0.Final");
session.resolveDirectMavenArtifact("io.undertow", "undertow-core", null, null, "1.0.0.Final");
session.resolveDirectMavenArtifact("io.undertow", "undertow-core", null, null, "2.0.0.Final");

Channel recordedChannel = session.getRecordedChannel();
System.out.println(ChannelMapper.toYaml(recordedChannel));

Collection<Stream> streams = recordedChannel.getStreams();
assertEquals(1, streams.size());
Stream stream = streams.iterator().next();
assertEquals("io.undertow", stream.getGroupId());
assertEquals("undertow-core", stream.getArtifactId());
assertNull(stream.getVersion());
assertNull(stream.getVersionPattern());
Map<String, Pattern> versions = stream.getVersions();
assertNotNull(versions);
assertEquals(2, versions.size());
assertTrue(versions.containsKey(Pattern.quote("1.0.0.Final")));
assertTrue(versions.containsKey(Pattern.quote("2.0.0.Final")));


// let's test the recorded channel serialization
List<Channel> channels = ChannelMapper.fromString(ChannelMapper.toYaml(recordedChannel));
assertEquals(1, channels.size());
Channel readChannel = channels.get(0);

try (ChannelSession session1 = new ChannelSession(List.of(readChannel), factory)) {
MavenArtifact mavenArtifact_100Final = session1.resolveMavenArtifact("io.undertow", "undertow-core", null, null, "1.0.0.Final");
assertEquals("1.0.0.Final", mavenArtifact_100Final.getVersion());

MavenArtifact mavenArtifact_200Final = session1.resolveMavenArtifact("io.undertow", "undertow-core", null, null, "2.0.0.Final");
assertEquals("2.0.0.Final", mavenArtifact_200Final.getVersion());
}

}

}

private static void assertStreamExistsFor(Collection<Stream> streams, String groupId, String artifactId, String version) {
Optional<Stream> stream = streams.stream().filter(s -> s.getGroupId().equals(groupId) &&
s.getArtifactId().equals(artifactId) &&
Expand Down
Loading