Skip to content

Commit

Permalink
Merge pull request quarkusio#3492 from aureamunoz/issue3437-spring-an…
Browse files Browse the repository at this point in the history
…notation-project-creation

feat: spring annotations project creation.
  • Loading branch information
geoand authored Aug 22, 2019
2 parents cb29369 + 798c10e commit f9ea1a8
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static io.quarkus.generators.ProjectGenerator.BUILD_TOOL;
import static io.quarkus.generators.ProjectGenerator.CLASS_NAME;
import static io.quarkus.generators.ProjectGenerator.IS_SPRING;
import static io.quarkus.generators.ProjectGenerator.PACKAGE_NAME;
import static io.quarkus.generators.ProjectGenerator.PROJECT_ARTIFACT_ID;
import static io.quarkus.generators.ProjectGenerator.PROJECT_GROUP_ID;
Expand Down Expand Up @@ -38,6 +39,7 @@ public class CreateProject {
private SourceType sourceType = SourceType.JAVA;
private BuildTool buildTool = BuildTool.MAVEN;
private String className;
private Set<String> extensions;

private Model model;

Expand Down Expand Up @@ -70,6 +72,11 @@ public CreateProject className(String className) {
return this;
}

public CreateProject extensions(Set<String> extensions) {
this.extensions = extensions;
return this;
}

public CreateProject buildTool(BuildTool buildTool) {
this.buildTool = buildTool;
return this;
Expand All @@ -93,6 +100,10 @@ public boolean doCreateProject(final Map<String, Object> context) throws IOExcep
context.put(SOURCE_TYPE, sourceType);
context.put(BUILD_TOOL, buildTool);

if (extensions != null && extensions.stream().anyMatch(e -> e.toLowerCase().contains("spring-web"))) {
context.put(IS_SPRING, Boolean.TRUE);
}

if (className != null) {
className = sourceType.stripExtensionFrom(className);
int idx = className.lastIndexOf('.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public interface ProjectGenerator {
String BUILD_TOOL = "build_tool";
String ADDITIONAL_GITIGNORE_ENTRIES = "additional_gitignore_entries";
String CLASS_NAME = "class_name";
String IS_SPRING = "is_spring";
String RESOURCE_PATH = "path";

String getName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public enum SourceType {

private static final String BUILD_FILE_RESOURCE_TEMPLATE = "templates/%s/%s/%s-template.ftl";
private static final String RESOURCE_TEMPLATE = "templates/%s/%s/resource-template.ftl";
private static final String SPRING_CONTROLLER_TEMPLATE = "templates/%s/%s/spring-controller-template.ftl";
private static final String TEST_RESOURCE_TEMPLATE = "templates/%s/%s/test-resource-template.ftl";
private static final String NATIVE_TEST_RESOURCE_TEMPLATE = "templates/%s/%s/native-test-resource-template.ftl";

Expand Down Expand Up @@ -48,6 +49,10 @@ public String getSrcResourceTemplate(String templateName) {
return computeTemplateFile(RESOURCE_TEMPLATE, templateName);
}

public String getSrcSpringControllerTemplate(String templateName) {
return computeTemplateFile(SPRING_CONTROLLER_TEMPLATE, templateName);
}

public String getTestResourceTemplate(String templateName) {
return computeTemplateFile(TEST_RESOURCE_TEMPLATE, templateName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,12 @@ private void createClasses() throws IOException {
String testClassFile = testMainPath + '/' + className + "Test" + extension;
String itTestClassFile = testMainPath + '/' + "Native" + className + "IT" + extension;
String name = getName();
generate(type.getSrcResourceTemplate(name), context, classFile, "resource code");
String srcResourceTemplate = type.getSrcResourceTemplate(name);
Object isSpring = context.get(IS_SPRING);
if (isSpring != null && (Boolean) context.get(IS_SPRING).equals(Boolean.TRUE)) {
srcResourceTemplate = type.getSrcSpringControllerTemplate(name);
}
generate(srcResourceTemplate, context, classFile, "resource code");
generate(type.getTestResourceTemplate(name), context, testClassFile, "test code");
generate(type.getNativeTestResourceTemplate(name), context, itTestClassFile, "IT code");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ${package_name};

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PathVariable;


@RestController
@RequestMapping("${path}")
public class ${class_name} {

@GetMapping
public String hello() {
return "hello";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.nio.file.Path;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -174,6 +175,50 @@ public void createOnTopPomWithResource() throws IOException {
assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).exists();
assertThat(new File(testDir, "src/main/java")).isDirectory();
assertThat(new File(testDir, "src/main/java/org/foo/MyResource.java")).isFile();
assertThat(contentOf(new File(testDir, "src/main/java/org/foo/MyResource.java"), "UTF-8"))
.contains("@Path", "@GET", "@Produces");
assertThat(new File(testDir, "src/test/java")).isDirectory();
assertThat(new File(testDir, "src/test/java/org/foo/MyResourceTest.java")).isFile();
assertThat(new File(testDir, "src/test/java/org/foo/NativeMyResourceIT.java")).isFile();

assertThat(contentOf(new File(testDir, "pom.xml"))).contains(getBomArtifactId());

}

@Test
public void createOnTopPomWithSpringController() throws IOException {
final File testDir = new File("target/existing");
delete(testDir);
testDir.mkdirs();

Model model = new Model();
model.setModelVersion("4.0.0");
model.setGroupId("org.acme");
model.setArtifactId("foobar");
model.setVersion("10.1.2");
final File pom = new File(testDir, "pom.xml");
MojoUtils.write(model, pom);
HashSet<String> extensions = new HashSet<>();
extensions.add("spring-web");
final CreateProject createProject = new CreateProject(new FileProjectWriter(testDir)).groupId("something.is")
.artifactId("wrong")
.className("org.foo.MyResource")
.version("1.0.0-SNAPSHOT")
.extensions(extensions);

Assertions.assertTrue(createProject.doCreateProject(new HashMap<>()));

assertThat(contentOf(pom, "UTF-8"))
.contains(getPluginArtifactId(), QUARKUS_VERSION_PROPERTY, getPluginGroupId());
assertThat(new File(testDir, "src/main/java")).isDirectory();
assertThat(new File(testDir, "src/test/java")).isDirectory();

assertThat(new File(testDir, "src/main/resources/application.properties")).exists();
assertThat(new File(testDir, "src/main/resources/META-INF/resources/index.html")).exists();
assertThat(new File(testDir, "src/main/java")).isDirectory();
assertThat(new File(testDir, "src/main/java/org/foo/MyResource.java")).isFile();
assertThat(contentOf(new File(testDir, "src/main/java/org/foo/MyResource.java"), "UTF-8"))
.contains("@RestController", "@RequestMapping", "@GetMapping");
assertThat(new File(testDir, "src/test/java")).isDirectory();
assertThat(new File(testDir, "src/test/java/org/foo/MyResourceTest.java")).isFile();
assertThat(new File(testDir, "src/test/java/org/foo/NativeMyResourceIT.java")).isFile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.io.File;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -61,7 +62,7 @@ void generateMultipleTimes() throws InterruptedException {

@Test
@DisplayName("Should generate project files with basic context")
void generateFiles() throws Exception {
void generateFilesWithJaxRsResource() throws Exception {
final ProjectWriter mockWriter = mock(ProjectWriter.class);
final BasicRestProjectGenerator basicRestProjectGenerator = new BasicRestProjectGenerator();

Expand Down Expand Up @@ -96,4 +97,24 @@ void generateFiles() throws Exception {
verify(mockWriter, times(1)).write(eq(".dockerignore"), anyString());
}

@Test
@DisplayName("Should generate project files with basic spring web context")
void generateFilesWithSpringControllerResource() throws Exception {
final ProjectWriter mockWriter = mock(ProjectWriter.class);
final BasicRestProjectGenerator basicRestProjectGenerator = new BasicRestProjectGenerator();

when(mockWriter.mkdirs(anyString())).thenAnswer(invocationOnMock -> invocationOnMock.getArgument(0, String.class));

Map<String, Object> springContext = new HashMap<>();
springContext.putAll(BASIC_PROJECT_CONTEXT);
springContext.put(IS_SPRING, Boolean.TRUE);
basicRestProjectGenerator.generate(mockWriter, springContext);

verify(mockWriter, times(1)).write(eq("src/main/java/org/example/ExampleResource.java"),
argThat(argument -> argument.contains("@RequestMapping(\"/hello\")")));
verify(mockWriter, times(1)).write(eq("src/main/java/org/example/ExampleResource.java"),
argThat(argument -> argument.contains("@RestController")));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ public void execute() throws MojoExecutionException {
.sourceType(sourceType)
.className(className)
.buildTool(buildToolEnum)
.extensions(extensions)
.doCreateProject(context);

File createdDependenciesBuildFile = new File(projectRoot, buildToolEnum.getDependenciesFile());
Expand Down
76 changes: 15 additions & 61 deletions docs/src/main/asciidoc/spring-web-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,49 +46,10 @@ mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \

This command generates a Maven project with a REST endpoint and imports the `spring-web` extension.

== GreetingController

== Writing the GreetingController

Before writing the `GreetingController`, let's first create a `Greeting` class that will be used as a response from the controller.
Furthermore let's create a Spring bean called `GreetingBean` that will be used by the controller.

[source,java]
----
package org.acme.spring.web;
public class Greeting {
private final String message;
public Greeting(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
----


[source,java]
----
package org.acme.spring.web;
import org.springframework.stereotype.Service;
@Service
public class GreetingBean {
public String greet(String input) {
return "HELLO " + input.toUpperCase() + "!";
}
}
----

The Quarkus Maven plugin automatically generated a `src/main/java/org/acme/spring/web/GreetingController.java` file.
However since we are going to be using Spring Web annotations to define our REST endpoint (instead of the JAX-RS ones used by default), we need to change
the `GreetingController` to have the following content:
The Quarkus maven plugin automatically generated a controller with the Spring Web annotations to define our REST endpoint (instead of the JAX-RS ones used by default)
The `src/main/java/org/acme/spring/web/GreetingController.java` file looks as follows:

[source,java]
----
Expand All @@ -103,23 +64,16 @@ import org.springframework.web.bind.annotation.PathVariable;
@RequestMapping("/greeting")
public class GreetingController {
private final GreetingBean greetingBean;
public GreetingController(GreetingBean greetingBean) {
this.greetingBean = greetingBean;
}
@GetMapping("/{name}")
public Greeting hello(@PathVariable(name = "name") String name) {
return new Greeting(greetingBean.greet(name));
@GetMapping
public String hello() {
return "hello";
}
}
----

== Update the test

We also need to update the functional test to reflect the changes made to the endpoint. Change its content to:
== GreetingControllerTest

Note that a test for the controller has been created as well:

[source, java]
----
Expand All @@ -135,12 +89,12 @@ import static org.hamcrest.CoreMatchers.is;
public class GreetingControllerTest {
@Test
public void testGreeting() {
public void testHelloEndpoint() {
given()
.when().get("/greeting/world")
.then()
.statusCode(200)
.body("message", is("HELLO WORLD!"));
.when().get("/greeting")
.then()
.statusCode(200)
.body(is("hello"));
}
}
Expand All @@ -149,9 +103,9 @@ public class GreetingControllerTest {
== Package and run the application

Run the application with: `./mvnw compile quarkus:dev`.
Open your browser to http://localhost:8080/greeting/world.
Open your browser to http://localhost:8080/greeting.

The result should be: `{"message": "HELLO WORLD!"}`.
The result should be: `{"message": "hello"}`.

== Run the application as a native executable

Expand Down

0 comments on commit f9ea1a8

Please sign in to comment.