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

Add mutiny support #189

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
54901cc
Merge pull request #1 from vert-x3/master
wowselim May 3, 2021
0c93937
Merge branch 'vert-x3:master' into master
wowselim May 27, 2021
6fafcbe
Merge branch 'vert-x3:master' into master
wowselim Jun 10, 2021
9d90949
add support for mutiny projects
wowselim Jun 17, 2021
647c80f
add separate stack for mutiny
wowselim Jun 20, 2021
309f536
fix wrong flavor name in templates
wowselim Jun 20, 2021
071c09f
remove obsolete artifact id prefix
wowselim Jun 20, 2021
75b2527
add flavor option to README.md
wowselim Jun 20, 2021
4e123d5
fix bug where mutiny dependencies were not checked
wowselim Jun 20, 2021
2cd1c19
remove "default" text from flavor in README.md
wowselim Jun 20, 2021
34c507d
fix bug with mixed mutiny & vertx deps
wowselim Jun 20, 2021
9730547
fix version bug with mixed mutiny & vertx deps
wowselim Jun 20, 2021
c86dfec
ignore isVertxDependency in jackson
wowselim Jun 20, 2021
b0b31e6
fix bug where groupId ends up wrong
wowselim Jun 26, 2021
3ca065d
make sure flavor deps are handled correctly
wowselim Jun 26, 2021
3575d46
Merge branch 'master' into add-mutiny-support
wowselim Jul 3, 2021
b886b1d
use Vert.x API instead of Flavor for website
wowselim Jul 5, 2021
e098a10
remove flavor ids from project constants
wowselim Jul 5, 2021
bdd8041
remove flavor from http request examples
wowselim Jul 5, 2021
24a9001
bump mutiny version to 2.8.0
wowselim Jul 5, 2021
03b4d28
switch from two stacks to boolean flag for mutiny bindings
wowselim Jul 5, 2021
fa9cb0a
add generator test for flavors
wowselim Jul 5, 2021
90a3fb9
add info about default flavor to readme
wowselim Jul 5, 2021
dc01e0c
add error msg for unsatisfiable dependencies
wowselim Jul 8, 2021
b231d26
bump mutiny version to 2.9.0
wowselim Jul 8, 2021
aceac49
add verticle & test code for mutiny flavor
wowselim Jul 8, 2021
f9907d0
set junit5 flag for mutiny projects
wowselim Jul 8, 2021
7a11188
fix bug that failed mutiny projects
wowselim Jul 8, 2021
9b85004
include flavor in VertxProject#toString
wowselim Jul 9, 2021
37612ce
don't include vertx depchain for mutiny
wowselim Jul 9, 2021
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ You can provide the following query parameters to customize the project
- `vertxDependencies`: a comma separated list of artifactIds of the vert.x modules
- `packageName`: code package name, derived from `groupId` and `artifactId` by default
- `jdkVersion`: which version of the JDK to use, defaults to `1.8`
- `flavor`: `vert.x` or `mutiny`, defaults to `vert.x`

Full example:

Expand Down
75 changes: 68 additions & 7 deletions src/main/java/io/vertx/starter/ValidationHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public class ValidationHandler implements Handler<RoutingContext> {
private final JsonObject defaults;
private final Set<String> versions;
private final Map<String, List<String>> exclusions;
private final Set<String> dependencies;
private final Map<String, Dependency> vertxStackDependencies;
private final Map<String, Dependency> mutinyStackDependencies;

public ValidationHandler(JsonObject defaults, JsonArray versions, JsonArray stack) {
this.defaults = defaults;
Expand All @@ -57,12 +58,27 @@ public ValidationHandler(JsonObject defaults, JsonArray versions, JsonArray stac
obj -> obj.getString("number"),
obj -> obj.getJsonArray("exclusions", new JsonArray()).stream().map(String.class::cast).collect(toList()))
);
dependencies = stack.stream()
vertxStackDependencies = stack.stream()
.map(JsonObject.class::cast)
.flatMap(category -> category.getJsonArray("items").stream())
.map(JsonObject.class::cast)
.map(item -> item.getString("artifactId"))
.collect(toSet());
.collect(toMap(
item -> item.getString("artifactId"),
item -> new Dependency()
.setGroupId(item.getString("groupId"))
.setArtifactId(item.getString("artifactId")))
);
mutinyStackDependencies = stack.stream()
.map(JsonObject.class::cast)
.flatMap(category -> category.getJsonArray("items").stream())
.map(JsonObject.class::cast)
.filter(item -> item.getBoolean("mutinyBindings"))
.collect(toMap(
item -> item.getString("artifactId"),
item -> new Dependency()
.setGroupId("io.smallrye.reactive")
.setArtifactId("smallrye-mutiny-" + item.getString("artifactId")))
);
}

@Override
Expand All @@ -86,6 +102,10 @@ public void handle(RoutingContext rc) {
return;
}

if (!validateAndSetEnum(rc, FLAVOR, ProjectFlavor::fromId, vertxProject::setFlavor)) {
return;
}

String vertxVersion = getQueryParam(rc, VERTX_VERSION);
if (isNotBlank(vertxVersion)) {
if (!versions.contains(vertxVersion)) {
Expand All @@ -103,8 +123,26 @@ public void handle(RoutingContext rc) {
.map(String::toLowerCase)
.collect(toSet());

if (!dependencies.containsAll(vertxDependencies) ||
!Collections.disjoint(exclusions.get(vertxProject.getVertxVersion()), vertxDependencies)) {
Set<String> unsatisfiableDependencies;
if (vertxProject.getFlavor() == ProjectFlavor.VERTX) {
unsatisfiableDependencies = vertxDependencies.stream()
.filter(dependency -> !vertxStackDependencies.containsKey(dependency))
.collect(toSet());
} else if (vertxProject.getFlavor() == ProjectFlavor.MUTINY) {
unsatisfiableDependencies = vertxDependencies.stream()
.filter(artifactId -> !isArtifactAvailableForMutinyFlavor(artifactId))
.collect(toSet());
} else {
throw new IllegalArgumentException("There's no stack for flavor " + vertxProject.getFlavor());
}

if (!unsatisfiableDependencies.isEmpty()) {
String capitalizedProjectFlavor = vertxProject.getFlavor().capitalizedId();
String message = "The following artifacts are not available for the Vert.x API '" + capitalizedProjectFlavor + "': " + unsatisfiableDependencies;
WebVerticle.fail(rc, 400, message);
}

if (!Collections.disjoint(exclusions.get(vertxProject.getVertxVersion()), vertxDependencies)) {
fail(rc, VERTX_DEPENDENCIES, deps);
return;
}
Expand All @@ -114,7 +152,25 @@ public void handle(RoutingContext rc) {
return;
}

vertxProject.setVertxDependencies(vertxDependencies);
Set<Dependency> flavoredDependencies;
if (vertxProject.getFlavor() == ProjectFlavor.VERTX) {
flavoredDependencies = vertxDependencies.stream()
.map(vertxStackDependencies::get)
.collect(toSet());
} else if (vertxProject.getFlavor() == ProjectFlavor.MUTINY) {
flavoredDependencies = vertxDependencies.stream()
.map(dependency -> mutinyStackDependencies.keySet().stream()
.filter(mutinyDependency -> mutinyDependency.endsWith(dependency))
.findFirst())
.filter(Optional::isPresent)
.map(Optional::get)
.map(mutinyStackDependencies::get)
.collect(toSet());
} else {
throw new IllegalArgumentException("Unknown project flavor " + vertxProject.getFlavor());
}

vertxProject.setVertxDependencies(flavoredDependencies);
}

ArchiveFormat archiveFormat = ArchiveFormat.fromFilename(rc.request().path());
Expand All @@ -141,6 +197,11 @@ public void handle(RoutingContext rc) {
rc.next();
}

private boolean isArtifactAvailableForMutinyFlavor(String artifactId) {
return mutinyStackDependencies.keySet().stream()
.anyMatch(dependency -> dependency.endsWith(artifactId));
}

private boolean validateAndSetId(RoutingContext rc, String name, Consumer<String> setter) {
String value = getQueryParam(rc, name);
if (isNotBlank(value)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ public interface ProjectConstants {
String ARCHIVE_FORMAT = "archiveFormat";
String PACKAGE_NAME = "packageName";
String JDK_VERSION = "jdkVersion";
String FLAVOR = "flavor";
}
54 changes: 54 additions & 0 deletions src/main/java/io/vertx/starter/model/Dependency.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.vertx.starter.model;

import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.Objects;

public class Dependency {
private String groupId;
private String artifactId;

public String getGroupId() {
return groupId;
}

public Dependency setGroupId(String groupId) {
this.groupId = groupId;
return this;
}

public String getArtifactId() {
return artifactId;
}

public Dependency setArtifactId(String artifactId) {
this.artifactId = artifactId;
return this;
}

@JsonIgnore
public boolean isVertxDependency() {
return ProjectFlavor.VERTX.getGroupId().equals(groupId);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dependency that = (Dependency) o;
return Objects.equals(groupId, that.groupId) && Objects.equals(artifactId, that.artifactId);
}

@Override
public int hashCode() {
return Objects.hash(groupId, artifactId);
}

@Override
public String toString() {
return "Dependency{" +
"groupId='" + groupId + '\'' +
", artifactId='" + artifactId + '\'' +
'}';
}
}
44 changes: 44 additions & 0 deletions src/main/java/io/vertx/starter/model/ProjectFlavor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.vertx.starter.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonValue;

import java.util.Locale;

public enum ProjectFlavor {
VERTX("vert.x", "io.vertx"),
MUTINY("mutiny", "io.smallrye.reactive");

private final String id;
private final String groupId;

ProjectFlavor(String id, String groupId) {
this.id = id;
this.groupId = groupId;
}

@JsonValue
public String getId() {
return id;
}

public String getGroupId() {
return groupId;
}

@JsonIgnore
public String capitalizedId() {
return id.substring(0, 1).toUpperCase(Locale.ROOT) + id.substring(1);
}

public static ProjectFlavor fromId(String id) {
switch (id.toLowerCase(Locale.ROOT)) {
case "mutiny":
return MUTINY;
case "vert.x":
return VERTX;
default:
return null;
}
}
}
17 changes: 14 additions & 3 deletions src/main/java/io/vertx/starter/model/VertxProject.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ public class VertxProject {
private Language language;
private BuildTool buildTool;
private String vertxVersion;
private Set<String> vertxDependencies;
private Set<Dependency> vertxDependencies;
private ArchiveFormat archiveFormat;
private String packageName;
private JdkVersion jdkVersion;
private String operatingSystem;
private Instant createdOn;
private ProjectFlavor flavor;

public String getId() {
return id;
Expand Down Expand Up @@ -88,11 +89,11 @@ public VertxProject setVertxVersion(String vertxVersion) {
return this;
}

public Set<String> getVertxDependencies() {
public Set<Dependency> getVertxDependencies() {
return vertxDependencies;
}

public VertxProject setVertxDependencies(Set<String> vertxDependencies) {
public VertxProject setVertxDependencies(Set<Dependency> vertxDependencies) {
this.vertxDependencies = vertxDependencies;
return this;
}
Expand Down Expand Up @@ -142,6 +143,15 @@ public VertxProject setCreatedOn(Instant createdOn) {
return this;
}

public ProjectFlavor getFlavor() {
return flavor;
}

public VertxProject setFlavor(ProjectFlavor flavor) {
this.flavor = flavor;
return this;
}

@Override
public String toString() {
// DO NOT RETURN USER RELATED-DATA (groupId, artifactId, packageName)
Expand All @@ -155,6 +165,7 @@ public String toString() {
", jdkVersion=" + jdkVersion +
", operatingSystem=" + operatingSystem +
", createdOn=" + createdOn +
", flavor='" + flavor + '\'' +
'}';
}
}
30 changes: 17 additions & 13 deletions src/main/java/io/vertx/starter/service/GeneratorService.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import io.vertx.core.buffer.Buffer;
import io.vertx.ext.web.templ.freemarker.FreeMarkerTemplateEngine;
import io.vertx.starter.model.ArchiveFormat;
import io.vertx.starter.model.Dependency;
import io.vertx.starter.model.Language;
import io.vertx.starter.model.ProjectFlavor;
import io.vertx.starter.model.VertxProject;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
Expand Down Expand Up @@ -117,26 +119,26 @@ private void createProject(VertxProject project, TempDir tempDir) throws IOExcep
Language language = project.getLanguage();
ctx.put("language", language.name().toLowerCase());
ctx.put("vertxVersion", project.getVertxVersion());
Set<String> vertxDependencies = project.getVertxDependencies();
Set<Dependency> vertxDependencies = project.getVertxDependencies();
if (vertxDependencies == null) {
vertxDependencies = new HashSet<>();
}
boolean hasVertxUnit = vertxDependencies.remove("vertx-unit");
boolean hasVertxUnit = vertxDependencies
.removeIf(dependency -> dependency.getArtifactId().endsWith("vertx-unit"));
ctx.put("hasVertxUnit", hasVertxUnit);
boolean hasVertxJUnit5 = vertxDependencies.remove("vertx-junit5") || !hasVertxUnit;
ctx.put("hasVertxJUnit5", hasVertxJUnit5);
boolean hasVertxJUnit5 = vertxDependencies
.removeIf(dependency -> dependency.getArtifactId().endsWith("vertx-junit5")) || !hasVertxUnit;
ctx.put("hasVertxJUnit5", hasVertxJUnit5 || project.getFlavor() == ProjectFlavor.MUTINY);
if (hasVertxUnit && hasVertxJUnit5) {
throw new RuntimeException("You cannot generate a project which depends on both vertx-unit and vertx-junit5.");
}
vertxDependencies.addAll(language.getLanguageDependencies());
ctx.put("languageDependencies", language.getLanguageDependencies());
ctx.put("vertxDependencies", vertxDependencies);
ctx.put("flavor", project.getFlavor().getId());
String packageName = packageName(project);
ctx.put("packageName", packageName);
ctx.put("jdkVersion", project.getJdkVersion().getValue());

Path tempDirPath = tempDir.path();
String tempDirPathStr = tempDirPath.toString();

copy(tempDir, "files", "_editorconfig");
copy(tempDir, "files", "_gitignore");

Expand All @@ -163,9 +165,11 @@ private void createProject(VertxProject project, TempDir tempDir) throws IOExcep

String packageDir = packageName.replace('.', '/');
String srcDir = "src/main/" + language.getName();
render(tempDir, ctx, srcDir, "MainVerticle" + language.getExtension(), srcDir + "/" + packageDir);
String srcFile = "MainVerticle." + project.getFlavor().getId() + language.getExtension();
render(tempDir, ctx, srcDir, srcFile, srcDir + "/" + packageDir, "MainVerticle" + language.getExtension());
String testSrcDir = "src/test/" + language.getName();
render(tempDir, ctx, testSrcDir, "TestMainVerticle" + language.getExtension(), testSrcDir + "/" + packageDir);
String testSrcFile = "TestMainVerticle." + project.getFlavor().getId() + language.getExtension();
render(tempDir, ctx, testSrcDir, testSrcFile, testSrcDir + "/" + packageDir, "TestMainVerticle" + language.getExtension());

render(tempDir, ctx, ".", "README.adoc");
}
Expand All @@ -191,14 +195,14 @@ private void copy(TempDir tempDir, String base, String filename) throws IOExcept
}

private void render(TempDir tempDir, Map<String, Object> ctx, String sourceDir, String filename) throws IOException {
render(tempDir, ctx, sourceDir, filename, sourceDir);
render(tempDir, ctx, sourceDir, filename, sourceDir, filename);
}

private void render(TempDir tempDir, Map<String, Object> ctx, String sourceDir, String filename, String destDir) throws IOException {
private void render(TempDir tempDir, Map<String, Object> ctx, String sourceDir, String filename, String destDir, String destFilename) throws IOException {
Path dest = tempDir.path().resolve(destDir);
Files.createDirectories(dest);
Buffer data = renderBlocking(ctx, "templates/" + sourceDir + "/" + filename + ".ftl");
vertx.fileSystem().writeFileBlocking(dest.resolve(filename).toString(), data);
vertx.fileSystem().writeFileBlocking(dest.resolve(destFilename).toString(), data);
}

private Buffer renderBlocking(Map<String, Object> context, String templateFileName) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/vertx/starter/service/MetadataHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.vertx.starter.model.BuildTool;
import io.vertx.starter.model.JdkVersion;
import io.vertx.starter.model.Language;
import io.vertx.starter.model.ProjectFlavor;

import java.util.Arrays;
import java.util.function.Function;
Expand All @@ -44,6 +45,7 @@ public MetadataHandler(JsonObject defaults, JsonArray versions, JsonArray stack)
.put("buildTools", values(BuildTool.values(), BuildTool::getValue))
.put("languages", values(Language.values(), Language::getName))
.put("jdkVersions", values(JdkVersion.values(), JdkVersion::getValue))
.put("flavors", values(ProjectFlavor.values(), ProjectFlavor::getId))
.put("vertxDependencies", stack) // deprecated
.put("vertxVersions", versions.stream() // deprecated
.map(JsonObject.class::cast)
Expand Down
Loading