diff --git a/devtools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java b/devtools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java index 9ce9b1342f834..3ae7b03939a04 100644 --- a/devtools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java +++ b/devtools/common/src/main/java/io/quarkus/cli/commands/CreateProject.java @@ -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; @@ -38,6 +39,7 @@ public class CreateProject { private SourceType sourceType = SourceType.JAVA; private BuildTool buildTool = BuildTool.MAVEN; private String className; + private Set extensions; private Model model; @@ -70,6 +72,11 @@ public CreateProject className(String className) { return this; } + public CreateProject extensions(Set extensions) { + this.extensions = extensions; + return this; + } + public CreateProject buildTool(BuildTool buildTool) { this.buildTool = buildTool; return this; @@ -93,6 +100,10 @@ public boolean doCreateProject(final Map 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('.'); diff --git a/devtools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java b/devtools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java index 3476041ea69a9..fdf1be28bb613 100644 --- a/devtools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java +++ b/devtools/common/src/main/java/io/quarkus/generators/ProjectGenerator.java @@ -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(); diff --git a/devtools/common/src/main/java/io/quarkus/generators/SourceType.java b/devtools/common/src/main/java/io/quarkus/generators/SourceType.java index a2366d66c8f38..e2f0e27423e48 100644 --- a/devtools/common/src/main/java/io/quarkus/generators/SourceType.java +++ b/devtools/common/src/main/java/io/quarkus/generators/SourceType.java @@ -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"; @@ -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); } diff --git a/devtools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java b/devtools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java index dacd619630bae..ad273b41248e3 100644 --- a/devtools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java +++ b/devtools/common/src/main/java/io/quarkus/generators/rest/BasicRestProjectGenerator.java @@ -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"); } diff --git a/devtools/common/src/main/resources/templates/basic-rest/java/spring-controller-template.ftl b/devtools/common/src/main/resources/templates/basic-rest/java/spring-controller-template.ftl new file mode 100644 index 0000000000000..d5565f3926eb6 --- /dev/null +++ b/devtools/common/src/main/resources/templates/basic-rest/java/spring-controller-template.ftl @@ -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"; + } +} diff --git a/devtools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java b/devtools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java index d3df05fa19030..b8f90eafefd43 100644 --- a/devtools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java +++ b/devtools/common/src/test/java/io/quarkus/cli/commands/CreateProjectTest.java @@ -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; @@ -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 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(); diff --git a/devtools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java b/devtools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java index 5548be20fb089..585c0bd898604 100644 --- a/devtools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java +++ b/devtools/common/src/test/java/io/quarkus/generators/rest/BasicRestProjectGeneratorTest.java @@ -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; @@ -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(); @@ -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 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"))); + + } + } diff --git a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java index 08df6ff7ac6bf..7876f1ac735a6 100644 --- a/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java +++ b/devtools/maven/src/main/java/io/quarkus/maven/CreateProjectMojo.java @@ -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()); diff --git a/docs/src/main/asciidoc/spring-web-guide.adoc b/docs/src/main/asciidoc/spring-web-guide.adoc index 7a125c39cc98f..f0671e47434dc 100644 --- a/docs/src/main/asciidoc/spring-web-guide.adoc +++ b/docs/src/main/asciidoc/spring-web-guide.adoc @@ -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] ---- @@ -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] ---- @@ -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")); } } @@ -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