Split controller endpoints

This commit splits MainController to a number of controllers with
dedicated responsibilities. This is a first step to make these features
more modular and potentially opt-in in the future.

Closes gh-994
This commit is contained in:
Stephane Nicoll 2019-08-26 14:15:20 +02:00
parent 282c92f76d
commit d9a20ed68c
20 changed files with 801 additions and 494 deletions

View File

@ -33,7 +33,10 @@ import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.InitializrMetadataBuilder;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import io.spring.initializr.metadata.InitializrProperties;
import io.spring.initializr.web.project.MainController;
import io.spring.initializr.web.controller.CommandLineMetadataController;
import io.spring.initializr.web.controller.ProjectGenerationController;
import io.spring.initializr.web.controller.ProjectMetadataController;
import io.spring.initializr.web.controller.SpringCliDistributionController;
import io.spring.initializr.web.project.ProjectGenerationInvoker;
import io.spring.initializr.web.project.ProjectRequestToDescriptionConverter;
import io.spring.initializr.web.support.DefaultDependencyMetadataProvider;
@ -139,11 +142,29 @@ public class InitializrAutoConfiguration {
@Bean
@ConditionalOnMissingBean
MainController initializrMainController(InitializrMetadataProvider metadataProvider,
TemplateRenderer templateRenderer, DependencyMetadataProvider dependencyMetadataProvider,
ProjectGenerationController projectGenerationController(InitializrMetadataProvider metadataProvider,
ProjectGenerationInvoker projectGenerationInvoker) {
return new MainController(metadataProvider, templateRenderer, dependencyMetadataProvider,
projectGenerationInvoker);
return new ProjectGenerationController(metadataProvider, projectGenerationInvoker);
}
@Bean
@ConditionalOnMissingBean
ProjectMetadataController projectMetadataController(InitializrMetadataProvider metadataProvider,
DependencyMetadataProvider dependencyMetadataProvider) {
return new ProjectMetadataController(metadataProvider, dependencyMetadataProvider);
}
@Bean
@ConditionalOnMissingBean
CommandLineMetadataController commandLineMetadataController(InitializrMetadataProvider metadataProvider,
TemplateRenderer templateRenderer) {
return new CommandLineMetadataController(metadataProvider, templateRenderer);
}
@Bean
@ConditionalOnMissingBean
SpringCliDistributionController cliDistributionController(InitializrMetadataProvider metadataProvider) {
return new SpringCliDistributionController(metadataProvider);
}
@Bean

View File

@ -14,16 +14,13 @@
* limitations under the License.
*/
package io.spring.initializr.web.project;
package io.spring.initializr.web.controller;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.util.DigestUtils;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
/**
@ -31,30 +28,16 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
*
* @author Stephane Nicoll
*/
public abstract class AbstractInitializrController {
public abstract class AbstractMetadataController {
protected final InitializrMetadataProvider metadataProvider;
private Boolean forceSsl;
protected AbstractInitializrController(InitializrMetadataProvider metadataProvider) {
protected AbstractMetadataController(InitializrMetadataProvider metadataProvider) {
this.metadataProvider = metadataProvider;
}
public boolean isForceSsl() {
if (this.forceSsl == null) {
this.forceSsl = this.metadataProvider.get().getConfiguration().getEnv().isForceSsl();
}
return this.forceSsl;
}
@ExceptionHandler
public void invalidProjectRequest(HttpServletResponse response, InvalidProjectRequestException ex)
throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
}
/**
* Generate a full URL of the service, mostly for use in templates.
* @return the app URL
@ -68,4 +51,18 @@ public abstract class AbstractInitializrController {
return builder.build().toString();
}
protected String createUniqueId(String content) {
StringBuilder builder = new StringBuilder();
DigestUtils.appendMd5DigestAsHex(content.getBytes(StandardCharsets.UTF_8), builder);
return builder.toString();
}
private boolean isForceSsl() {
if (this.forceSsl == null) {
this.forceSsl = this.metadataProvider.get().getConfiguration().getEnv().isForceSsl();
}
return this.forceSsl;
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web.controller;
import java.io.IOException;
import io.spring.initializr.generator.io.template.TemplateRenderer;
import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import io.spring.initializr.web.support.Agent;
import io.spring.initializr.web.support.Agent.AgentId;
import io.spring.initializr.web.support.CommandLineHelpGenerator;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* {@link Controller} that handles assistance for CLI support using a
* {@link CommandLineHelpGenerator}.
*
* @author Stephane Nicoll
*/
@Controller
public class CommandLineMetadataController extends AbstractMetadataController {
private final CommandLineHelpGenerator commandLineHelpGenerator;
public CommandLineMetadataController(InitializrMetadataProvider metadataProvider,
TemplateRenderer templateRenderer) {
super(metadataProvider);
this.commandLineHelpGenerator = new CommandLineHelpGenerator(templateRenderer);
}
@RequestMapping(path = "/", produces = "text/plain")
public ResponseEntity<String> serviceCapabilitiesText(
@RequestHeader(value = HttpHeaders.USER_AGENT, required = false) String userAgent) throws IOException {
String appUrl = generateAppUrl();
InitializrMetadata metadata = this.metadataProvider.get();
BodyBuilder builder = ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN);
if (userAgent != null) {
Agent agent = Agent.fromUserAgent(userAgent);
if (agent != null) {
if (AgentId.CURL.equals(agent.getId())) {
String content = this.commandLineHelpGenerator.generateCurlCapabilities(metadata, appUrl);
return builder.eTag(createUniqueId(content)).body(content);
}
if (AgentId.HTTPIE.equals(agent.getId())) {
String content = this.commandLineHelpGenerator.generateHttpieCapabilities(metadata, appUrl);
return builder.eTag(createUniqueId(content)).body(content);
}
if (AgentId.SPRING_BOOT_CLI.equals(agent.getId())) {
String content = this.commandLineHelpGenerator.generateSpringBootCliCapabilities(metadata, appUrl);
return builder.eTag(createUniqueId(content)).body(content);
}
}
}
String content = this.commandLineHelpGenerator.generateGenericCapabilities(metadata, appUrl);
return builder.eTag(createUniqueId(content)).body(content);
}
}

View File

@ -14,39 +14,31 @@
* limitations under the License.
*/
package io.spring.initializr.web.project;
package io.spring.initializr.web.controller;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import javax.servlet.http.HttpServletResponse;
import io.spring.initializr.generator.buildsystem.BuildSystem;
import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem;
import io.spring.initializr.generator.io.template.TemplateRenderer;
import io.spring.initializr.generator.project.ProjectDescription;
import io.spring.initializr.generator.version.Version;
import io.spring.initializr.metadata.DependencyMetadata;
import io.spring.initializr.metadata.DependencyMetadataProvider;
import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import io.spring.initializr.web.mapper.DependencyMetadataV21JsonMapper;
import io.spring.initializr.web.mapper.InitializrMetadataJsonMapper;
import io.spring.initializr.web.mapper.InitializrMetadataV21JsonMapper;
import io.spring.initializr.web.mapper.InitializrMetadataV2JsonMapper;
import io.spring.initializr.web.mapper.InitializrMetadataVersion;
import io.spring.initializr.web.support.Agent;
import io.spring.initializr.web.support.Agent.AgentId;
import io.spring.initializr.web.support.CommandLineHelpGenerator;
import io.spring.initializr.web.project.InvalidProjectRequestException;
import io.spring.initializr.web.project.ProjectGenerationInvoker;
import io.spring.initializr.web.project.ProjectGenerationResult;
import io.spring.initializr.web.project.ProjectRequest;
import io.spring.initializr.web.project.WebProjectRequest;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
@ -58,48 +50,33 @@ import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.stereotype.Controller;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* The main initializr controller provides access to the configured metadata and serves as
* a central endpoint to generate projects or build files.
* {@link Controller} that provides endpoints for project generation.
*
* @author Dave Syer
* @author Stephane Nicoll
*/
@Controller
public class MainController extends AbstractInitializrController {
public class ProjectGenerationController {
private static final Log logger = LogFactory.getLog(MainController.class);
private static final Log logger = LogFactory.getLog(ProjectGenerationController.class);
/**
* HAL JSON content type.
*/
public static final MediaType HAL_JSON_CONTENT_TYPE = MediaType.parseMediaType("application/hal+json");
private final DependencyMetadataProvider dependencyMetadataProvider;
private final CommandLineHelpGenerator commandLineHelpGenerator;
private final InitializrMetadataProvider metadataProvider;
private final ProjectGenerationInvoker projectGenerationInvoker;
public MainController(InitializrMetadataProvider metadataProvider, TemplateRenderer templateRenderer,
DependencyMetadataProvider dependencyMetadataProvider, ProjectGenerationInvoker projectGenerationInvoker) {
super(metadataProvider);
this.dependencyMetadataProvider = dependencyMetadataProvider;
this.commandLineHelpGenerator = new CommandLineHelpGenerator(templateRenderer);
public ProjectGenerationController(InitializrMetadataProvider metadataProvider,
ProjectGenerationInvoker projectGenerationInvoker) {
this.metadataProvider = metadataProvider;
this.projectGenerationInvoker = projectGenerationInvoker;
}
@ -111,101 +88,10 @@ public class MainController extends AbstractInitializrController {
return request;
}
@RequestMapping(path = "/metadata/config", produces = "application/json")
@ResponseBody
public InitializrMetadata config() {
return this.metadataProvider.get();
}
@RequestMapping(path = "/", produces = "text/plain")
public ResponseEntity<String> serviceCapabilitiesText(
@RequestHeader(value = HttpHeaders.USER_AGENT, required = false) String userAgent) throws IOException {
String appUrl = generateAppUrl();
InitializrMetadata metadata = this.metadataProvider.get();
BodyBuilder builder = ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN);
if (userAgent != null) {
Agent agent = Agent.fromUserAgent(userAgent);
if (agent != null) {
if (AgentId.CURL.equals(agent.getId())) {
String content = this.commandLineHelpGenerator.generateCurlCapabilities(metadata, appUrl);
return builder.eTag(createUniqueId(content)).body(content);
}
if (AgentId.HTTPIE.equals(agent.getId())) {
String content = this.commandLineHelpGenerator.generateHttpieCapabilities(metadata, appUrl);
return builder.eTag(createUniqueId(content)).body(content);
}
if (AgentId.SPRING_BOOT_CLI.equals(agent.getId())) {
String content = this.commandLineHelpGenerator.generateSpringBootCliCapabilities(metadata, appUrl);
return builder.eTag(createUniqueId(content)).body(content);
}
}
}
String content = this.commandLineHelpGenerator.generateGenericCapabilities(metadata, appUrl);
return builder.eTag(createUniqueId(content)).body(content);
}
@RequestMapping(path = { "/", "/metadata/client" }, produces = "application/hal+json")
public ResponseEntity<String> serviceCapabilitiesHal() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1, HAL_JSON_CONTENT_TYPE);
}
@RequestMapping(path = { "/", "/metadata/client" },
produces = { "application/vnd.initializr.v2.1+json", "application/json" })
public ResponseEntity<String> serviceCapabilitiesV21() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1);
}
@RequestMapping(path = { "/", "/metadata/client" }, produces = "application/vnd.initializr.v2+json")
public ResponseEntity<String> serviceCapabilitiesV2() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2);
}
private ResponseEntity<String> serviceCapabilitiesFor(InitializrMetadataVersion version) {
return serviceCapabilitiesFor(version, version.getMediaType());
}
private ResponseEntity<String> serviceCapabilitiesFor(InitializrMetadataVersion version, MediaType contentType) {
String appUrl = generateAppUrl();
String content = getJsonMapper(version).write(this.metadataProvider.get(), appUrl);
return ResponseEntity.ok().contentType(contentType).eTag(createUniqueId(content)).varyBy("Accept")
.cacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)).body(content);
}
private static InitializrMetadataJsonMapper getJsonMapper(InitializrMetadataVersion version) {
switch (version) {
case V2:
return new InitializrMetadataV2JsonMapper();
default:
return new InitializrMetadataV21JsonMapper();
}
}
@RequestMapping(path = "/dependencies", produces = { "application/vnd.initializr.v2.1+json", "application/json" })
public ResponseEntity<String> dependenciesV21(@RequestParam(required = false) String bootVersion) {
return dependenciesFor(InitializrMetadataVersion.V2_1, bootVersion);
}
private ResponseEntity<String> dependenciesFor(InitializrMetadataVersion version, String bootVersion) {
InitializrMetadata metadata = this.metadataProvider.get();
Version v = (bootVersion != null) ? Version.parse(bootVersion)
: Version.parse(metadata.getBootVersions().getDefault().getId());
DependencyMetadata dependencyMetadata = this.dependencyMetadataProvider.get(metadata, v);
String content = new DependencyMetadataV21JsonMapper().write(dependencyMetadata);
return ResponseEntity.ok().contentType(version.getMediaType()).eTag(createUniqueId(content))
.cacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)).body(content);
}
@RequestMapping(path = { "/spring", "/spring.zip" })
public String spring() {
String url = this.metadataProvider.get().createCliDistributionURl("zip");
return "redirect:" + url;
}
@RequestMapping(path = { "/spring.tar.gz", "spring.tgz" })
public String springTgz() {
String url = this.metadataProvider.get().createCliDistributionURl("tar.gz");
return "redirect:" + url;
@ExceptionHandler
public void invalidProjectRequest(HttpServletResponse response, InvalidProjectRequestException ex)
throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
}
@RequestMapping(path = { "/pom", "/pom.xml" })
@ -229,7 +115,7 @@ public class MainController extends AbstractInitializrController {
public ResponseEntity<byte[]> springZip(ProjectRequest request) throws IOException {
ProjectGenerationResult result = this.projectGenerationInvoker.invokeProjectStructureGeneration(request);
Path archive = createArchive(result, "zip", ZipArchiveOutputStream::new, ZipArchiveEntry::new,
(entry, mode) -> entry.setUnixMode(mode));
ZipArchiveEntry::setUnixMode);
return upload(archive, result.getRootDirectory(), generateFileName(request, "zip"), "application/zip");
}
@ -238,7 +124,7 @@ public class MainController extends AbstractInitializrController {
public ResponseEntity<byte[]> springTgz(ProjectRequest request) throws IOException {
ProjectGenerationResult result = this.projectGenerationInvoker.invokeProjectStructureGeneration(request);
Path archive = createArchive(result, "tar.gz", this::createTarArchiveOutputStream, TarArchiveEntry::new,
(entry, mode) -> entry.setMode(mode));
TarArchiveEntry::setMode);
return upload(archive, result.getRootDirectory(), generateFileName(request, "tar.gz"),
"application/x-compress");
}
@ -327,10 +213,4 @@ public class MainController extends AbstractInitializrController {
.header("Content-Disposition", contentDispositionValue).body(content);
}
private String createUniqueId(String content) {
StringBuilder builder = new StringBuilder();
DigestUtils.appendMd5DigestAsHex(content.getBytes(StandardCharsets.UTF_8), builder);
return builder.toString();
}
}

View File

@ -0,0 +1,118 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web.controller;
import java.util.concurrent.TimeUnit;
import io.spring.initializr.generator.version.Version;
import io.spring.initializr.metadata.DependencyMetadata;
import io.spring.initializr.metadata.DependencyMetadataProvider;
import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import io.spring.initializr.web.mapper.DependencyMetadataV21JsonMapper;
import io.spring.initializr.web.mapper.InitializrMetadataJsonMapper;
import io.spring.initializr.web.mapper.InitializrMetadataV21JsonMapper;
import io.spring.initializr.web.mapper.InitializrMetadataV2JsonMapper;
import io.spring.initializr.web.mapper.InitializrMetadataVersion;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* {@link Controller} that exposes metadata and service configuration.
*
* @author Stephane Nicoll
*/
@Controller
public class ProjectMetadataController extends AbstractMetadataController {
/**
* HAL JSON content type.
*/
public static final MediaType HAL_JSON_CONTENT_TYPE = MediaType.parseMediaType("application/hal+json");
private final DependencyMetadataProvider dependencyMetadataProvider;
public ProjectMetadataController(InitializrMetadataProvider metadataProvider,
DependencyMetadataProvider dependencyMetadataProvider) {
super(metadataProvider);
this.dependencyMetadataProvider = dependencyMetadataProvider;
}
@RequestMapping(path = "/metadata/config", produces = "application/json")
@ResponseBody
public InitializrMetadata config() {
return this.metadataProvider.get();
}
@RequestMapping(path = { "/", "/metadata/client" }, produces = "application/hal+json")
public ResponseEntity<String> serviceCapabilitiesHal() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1, HAL_JSON_CONTENT_TYPE);
}
@RequestMapping(path = { "/", "/metadata/client" },
produces = { "application/vnd.initializr.v2.1+json", "application/json" })
public ResponseEntity<String> serviceCapabilitiesV21() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2_1);
}
@RequestMapping(path = { "/", "/metadata/client" }, produces = "application/vnd.initializr.v2+json")
public ResponseEntity<String> serviceCapabilitiesV2() {
return serviceCapabilitiesFor(InitializrMetadataVersion.V2);
}
@RequestMapping(path = "/dependencies", produces = { "application/vnd.initializr.v2.1+json", "application/json" })
public ResponseEntity<String> dependenciesV21(@RequestParam(required = false) String bootVersion) {
return dependenciesFor(InitializrMetadataVersion.V2_1, bootVersion);
}
private ResponseEntity<String> dependenciesFor(InitializrMetadataVersion version, String bootVersion) {
InitializrMetadata metadata = this.metadataProvider.get();
Version v = (bootVersion != null) ? Version.parse(bootVersion)
: Version.parse(metadata.getBootVersions().getDefault().getId());
DependencyMetadata dependencyMetadata = this.dependencyMetadataProvider.get(metadata, v);
String content = new DependencyMetadataV21JsonMapper().write(dependencyMetadata);
return ResponseEntity.ok().contentType(version.getMediaType()).eTag(createUniqueId(content))
.cacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)).body(content);
}
private ResponseEntity<String> serviceCapabilitiesFor(InitializrMetadataVersion version) {
return serviceCapabilitiesFor(version, version.getMediaType());
}
private ResponseEntity<String> serviceCapabilitiesFor(InitializrMetadataVersion version, MediaType contentType) {
String appUrl = generateAppUrl();
String content = getJsonMapper(version).write(this.metadataProvider.get(), appUrl);
return ResponseEntity.ok().contentType(contentType).eTag(createUniqueId(content)).varyBy("Accept")
.cacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)).body(content);
}
private static InitializrMetadataJsonMapper getJsonMapper(InitializrMetadataVersion version) {
switch (version) {
case V2:
return new InitializrMetadataV2JsonMapper();
default:
return new InitializrMetadataV21JsonMapper();
}
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web.controller;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* {@link Controller} that provides access to the Spring CLI.
*
* @author Stephane Nicoll
*/
@Controller
public class SpringCliDistributionController {
private final InitializrMetadataProvider metadataProvider;
public SpringCliDistributionController(InitializrMetadataProvider metadataProvider) {
this.metadataProvider = metadataProvider;
}
@RequestMapping(path = { "/spring", "/spring.zip" })
public String spring() {
String url = this.metadataProvider.get().createCliDistributionURl("zip");
return "redirect:" + url;
}
@RequestMapping(path = { "/spring.tar.gz", "spring.tgz" })
public String springTgz() {
String url = this.metadataProvider.get().createCliDistributionURl("tar.gz");
return "redirect:" + url;
}
}

View File

@ -19,7 +19,10 @@ package io.spring.initializr.web.autoconfigure;
import io.spring.initializr.generator.io.template.TemplateRenderer;
import io.spring.initializr.metadata.DependencyMetadataProvider;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import io.spring.initializr.web.project.MainController;
import io.spring.initializr.web.controller.CommandLineMetadataController;
import io.spring.initializr.web.controller.ProjectGenerationController;
import io.spring.initializr.web.controller.ProjectMetadataController;
import io.spring.initializr.web.controller.SpringCliDistributionController;
import io.spring.initializr.web.project.ProjectGenerationInvoker;
import io.spring.initializr.web.project.ProjectRequestToDescriptionConverter;
import io.spring.initializr.web.support.DefaultInitializrMetadataUpdateStrategy;
@ -131,7 +134,10 @@ class InitializrAutoConfigurationTests {
assertThat(context).hasSingleBean(InitializrWebConfig.class);
assertThat(context).hasSingleBean(ProjectGenerationInvoker.class);
assertThat(context).hasSingleBean(ProjectRequestToDescriptionConverter.class);
assertThat(context).hasSingleBean(MainController.class);
assertThat(context).hasSingleBean(ProjectGenerationController.class);
assertThat(context).hasSingleBean(ProjectMetadataController.class);
assertThat(context).hasSingleBean(CommandLineMetadataController.class);
assertThat(context).hasSingleBean(SpringCliDistributionController.class);
});
}
@ -139,7 +145,10 @@ class InitializrAutoConfigurationTests {
void webConfigurationConditionalOnWebApplication() {
this.contextRunner.run((context) -> {
assertThat(context).doesNotHaveBean(InitializrWebConfig.class);
assertThat(context).doesNotHaveBean(MainController.class);
assertThat(context).doesNotHaveBean(ProjectGenerationController.class);
assertThat(context).doesNotHaveBean(ProjectMetadataController.class);
assertThat(context).doesNotHaveBean(CommandLineMetadataController.class);
assertThat(context).doesNotHaveBean(SpringCliDistributionController.class);
});
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.spring.initializr.web.project;
package io.spring.initializr.web.controller;
import io.spring.initializr.generator.test.buildsystem.maven.MavenBuildAssert;
import io.spring.initializr.generator.test.project.ProjectStructure;

View File

@ -0,0 +1,129 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web.controller;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
import io.spring.initializr.web.AbstractInitializrIntegrationTests;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link CommandLineMetadataController}.
*
* @author Stephane Nicoll
*/
@ActiveProfiles("test-default")
public class CommandLineMetadataControllerIntegrationTests extends AbstractInitializrControllerIntegrationTests {
@Test
void curlReceivesTextByDefault() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "*/*");
validateCurlHelpContent(response);
}
@Test
// make sure curl can still receive metadata with json
void curlWithAcceptHeaderJson() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "application/json");
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void curlWithAcceptHeaderTextPlain() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "text/plain");
validateCurlHelpContent(response);
}
@Test
void httpieReceivesTextByDefault() {
ResponseEntity<String> response = invokeHome("HTTPie/0.8.0", "*/*");
validateHttpIeHelpContent(response);
}
@Test
// make sure curl can still receive metadata with json
void httpieWithAcceptHeaderJson() {
ResponseEntity<String> response = invokeHome("HTTPie/0.8.0", "application/json");
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void httpieWithAcceptHeaderTextPlain() {
ResponseEntity<String> response = invokeHome("HTTPie/0.8.0", "text/plain");
validateHttpIeHelpContent(response);
}
@Test
void unknownCliWithTextPlain() {
ResponseEntity<String> response = invokeHome(null, "text/plain");
validateGenericHelpContent(response);
}
@Test
void springBootCliReceivesJsonByDefault() {
ResponseEntity<String> response = invokeHome("SpringBootCli/1.2.0", "*/*");
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void springBootCliWithAcceptHeaderText() {
ResponseEntity<String> response = invokeHome("SpringBootCli/1.2.0", "text/plain");
validateSpringBootHelpContent(response);
}
@Test
void doNotForceSslByDefault() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "*/*");
String body = response.getBody();
assertThat(body).as("Must not force https").doesNotContain("https://");
}
private void validateCurlHelpContent(ResponseEntity<String> response) {
validateContentType(response, MediaType.TEXT_PLAIN);
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
assertThat(response.getBody()).contains("Spring Initializr", "Examples:", "curl");
}
private void validateHttpIeHelpContent(ResponseEntity<String> response) {
validateContentType(response, MediaType.TEXT_PLAIN);
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
assertThat(response.getBody()).contains("Spring Initializr", "Examples:", "http").doesNotContain("curl");
}
private void validateGenericHelpContent(ResponseEntity<String> response) {
validateContentType(response, MediaType.TEXT_PLAIN);
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
assertThat(response.getBody()).contains("Spring Initializr").doesNotContain("Examples:", "curl");
}
private void validateSpringBootHelpContent(ResponseEntity<String> response) {
validateContentType(response, MediaType.TEXT_PLAIN);
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
assertThat(response.getBody()).contains("Service capabilities", "Supported dependencies")
.doesNotContain("Examples:", "curl");
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web.controller;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
import org.junit.jupiter.api.Test;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link CommandLineMetadataController} with {@code forceSsl}
* enabled.
*
* @author Stephane Nicoll
*/
@ActiveProfiles({ "test-default", "test-ssl" })
public class CommandLineMetadataControllerSslIntegrationTests extends AbstractInitializrControllerIntegrationTests {
@Test
void forceSsl() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "*/*");
String body = response.getBody();
assertThat(body).as("Must force https").contains("https://start.spring.io/");
assertThat(body).as("Must force https").doesNotContain("http://");
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.spring.initializr.web.project;
package io.spring.initializr.web.controller;
import io.spring.initializr.generator.test.project.ProjectStructure;
import io.spring.initializr.web.AbstractFullStackInitializrIntegrationTests;
@ -23,10 +23,12 @@ import org.junit.jupiter.api.Test;
import org.springframework.test.context.ActiveProfiles;
/**
* Integration tests for {@link ProjectGenerationController} on a real http server.
*
* @author HaiTao Zhang
*/
@ActiveProfiles("test-default")
public class MainControllerArchiveIntegrationTests extends AbstractFullStackInitializrIntegrationTests {
public class ProjectGenerationControllerArchiveIntegrationTests extends AbstractFullStackInitializrIntegrationTests {
@Test
void baseDirectorySeparatedBySpace() {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.spring.initializr.web.project;
package io.spring.initializr.web.controller;
import io.spring.initializr.generator.test.buildsystem.maven.MavenBuildAssert;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
@ -23,10 +23,12 @@ import org.junit.jupiter.api.Test;
import org.springframework.test.context.ActiveProfiles;
/**
* Integration tests for {@link ProjectGenerationController} that uses custom defaults.
*
* @author Stephane Nicoll
*/
@ActiveProfiles({ "test-default", "test-custom-defaults" })
class MainControllerDefaultsIntegrationTests extends AbstractInitializrControllerIntegrationTests {
class ProjectGenerationControllerCustomDefaultsIntegrationTests extends AbstractInitializrControllerIntegrationTests {
// see defaults customization

View File

@ -14,36 +14,24 @@
* limitations under the License.
*/
package io.spring.initializr.web.project;
import java.net.URI;
package io.spring.initializr.web.controller;
import io.spring.initializr.generator.test.project.ProjectStructure;
import io.spring.initializr.metadata.Dependency;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests with custom environment.
* Integration tests for {@link ProjectGenerationController} with custom environment.
*
* @author Stephane Nicoll
*/
@ActiveProfiles({ "test-default", "test-custom-env" })
class MainControllerEnvIntegrationTests extends AbstractInitializrControllerIntegrationTests {
@Test
void downloadCliWithCustomRepository() throws Exception {
ResponseEntity<?> entity = getRestTemplate().getForEntity(createUrl("/spring"), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
String expected = "https://repo.spring.io/lib-release/org/springframework/boot/spring-boot-cli/2.1.4.RELEASE/spring-boot-cli-2.1.4.RELEASE-bin.zip";
assertThat(entity.getHeaders().getLocation()).isEqualTo(new URI(expected));
}
class ProjectGenerationControllerCustomEnvIntegrationTests extends AbstractInitializrControllerIntegrationTests {
@Test
void generateProjectWithInvalidName() {

View File

@ -14,23 +14,15 @@
* limitations under the License.
*/
package io.spring.initializr.web.project;
import java.net.URI;
import java.net.URISyntaxException;
package io.spring.initializr.web.controller;
import io.spring.initializr.generator.test.project.ProjectStructure;
import io.spring.initializr.metadata.Dependency;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
import io.spring.initializr.web.AbstractInitializrIntegrationTests;
import io.spring.initializr.web.mapper.InitializrMetadataVersion;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@ -41,10 +33,12 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* Integration tests for {@link ProjectGenerationController}.
*
* @author Stephane Nicoll
*/
@ActiveProfiles("test-default")
class MainControllerIntegrationTests extends AbstractInitializrControllerIntegrationTests {
class ProjectGenerationControllerIntegrationTests extends AbstractInitializrControllerIntegrationTests {
@Test
void simpleZipProject() {
@ -150,206 +144,6 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
assertHasWebResources(project);
}
@Test
void downloadCli() throws Exception {
assertSpringCliRedirect("/spring", "zip");
}
@Test
void downloadCliAsZip() throws Exception {
assertSpringCliRedirect("/spring.zip", "zip");
}
@Test
void downloadCliAsTarGz() throws Exception {
assertSpringCliRedirect("/spring.tar.gz", "tar.gz");
}
@Test
void downloadCliAsTgz() throws Exception {
assertSpringCliRedirect("/spring.tgz", "tar.gz");
}
private void assertSpringCliRedirect(String context, String extension) throws URISyntaxException {
ResponseEntity<?> entity = getRestTemplate().getForEntity(createUrl(context), ResponseEntity.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
String expected = "https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/2.1.4.RELEASE/spring-boot-cli-2.1.4.RELEASE-bin."
+ extension;
assertThat(entity.getHeaders().getLocation()).isEqualTo(new URI(expected));
}
@Test
void metadataWithNoAcceptHeader() {
// rest template sets application/json by default
ResponseEntity<String> response = invokeHome(null, "*/*");
validateCurrentMetadata(response);
}
@Test
@Disabled("Need a comparator that does not care about the number of elements in an array")
void currentMetadataCompatibleWithV2() {
ResponseEntity<String> response = invokeHome(null, "*/*");
validateMetadata(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE, "2.0.0",
JSONCompareMode.LENIENT);
}
@Test
void metadataWithV2AcceptHeader() {
ResponseEntity<String> response = invokeHome(null, "application/vnd.initializr.v2+json");
validateMetadata(response, InitializrMetadataVersion.V2.getMediaType(), "2.0.0", JSONCompareMode.STRICT);
}
@Test
void metadataWithCurrentAcceptHeader() {
getRequests().setFields("_links.maven-project", "dependencies.values[0]", "type.values[0]",
"javaVersion.values[0]", "packaging.values[0]", "bootVersion.values[0]", "language.values[0]");
ResponseEntity<String> response = invokeHome(null, "application/vnd.initializr.v2.1+json");
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void metadataWithSeveralAcceptHeader() {
ResponseEntity<String> response = invokeHome(null, "application/vnd.initializr.v2.1+json",
"application/vnd.initializr.v2+json");
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void metadataWithHalAcceptHeader() {
ResponseEntity<String> response = invokeHome(null, "application/hal+json");
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
validateContentType(response, MainController.HAL_JSON_CONTENT_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void metadataWithUnknownAcceptHeader() {
try {
invokeHome(null, "application/vnd.initializr.v5.4+json");
}
catch (HttpClientErrorException ex) {
assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.NOT_ACCEPTABLE);
}
}
@Test
void curlReceivesTextByDefault() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "*/*");
validateCurlHelpContent(response);
}
@Test
void curlCanStillDownloadZipArchive() {
ResponseEntity<byte[]> response = execute("/starter.zip", byte[].class, "curl/1.2.4", "*/*");
assertDefaultProject(projectFromArchive(response.getBody()));
}
@Test
void curlCanStillDownloadTgzArchive() {
ResponseEntity<byte[]> response = execute("/starter.tgz", byte[].class, "curl/1.2.4", "*/*");
assertDefaultProject(tgzProjectAssert(response.getBody()));
}
@Test
// make sure curl can still receive metadata with json
void curlWithAcceptHeaderJson() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "application/json");
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void curlWithAcceptHeaderTextPlain() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "text/plain");
validateCurlHelpContent(response);
}
@Test
void unknownAgentReceivesJsonByDefault() {
ResponseEntity<String> response = invokeHome("foo/1.0", "*/*");
validateCurrentMetadata(response);
}
@Test
void httpieReceivesTextByDefault() {
ResponseEntity<String> response = invokeHome("HTTPie/0.8.0", "*/*");
validateHttpIeHelpContent(response);
}
@Test
// make sure curl can still receive metadata with json
void httpieWithAcceptHeaderJson() {
ResponseEntity<String> response = invokeHome("HTTPie/0.8.0", "application/json");
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void httpieWithAcceptHeaderTextPlain() {
ResponseEntity<String> response = invokeHome("HTTPie/0.8.0", "text/plain");
validateHttpIeHelpContent(response);
}
@Test
void unknownCliWithTextPlain() {
ResponseEntity<String> response = invokeHome(null, "text/plain");
validateGenericHelpContent(response);
}
@Test
void springBootCliReceivesJsonByDefault() {
ResponseEntity<String> response = invokeHome("SpringBootCli/1.2.0", "*/*");
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void springBootCliWithAcceptHeaderText() {
ResponseEntity<String> response = invokeHome("SpringBootCli/1.2.0", "text/plain");
validateSpringBootHelpContent(response);
}
@Test
// Test that the current output is exactly what we expect
void validateCurrentProjectMetadata() {
validateCurrentMetadata(getMetadataJson());
}
@Test
void doNotForceSslByDefault() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "*/*");
String body = response.getBody();
assertThat(body).as("Must not force https").doesNotContain("https://");
}
private void validateCurlHelpContent(ResponseEntity<String> response) {
validateContentType(response, MediaType.TEXT_PLAIN);
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
assertThat(response.getBody()).contains("Spring Initializr", "Examples:", "curl");
}
private void validateHttpIeHelpContent(ResponseEntity<String> response) {
validateContentType(response, MediaType.TEXT_PLAIN);
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
assertThat(response.getBody()).contains("Spring Initializr", "Examples:", "http").doesNotContain("curl");
}
private void validateGenericHelpContent(ResponseEntity<String> response) {
validateContentType(response, MediaType.TEXT_PLAIN);
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
assertThat(response.getBody()).contains("Spring Initializr").doesNotContain("Examples:", "curl");
}
private void validateSpringBootHelpContent(ResponseEntity<String> response) {
validateContentType(response, MediaType.TEXT_PLAIN);
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
assertThat(response.getBody()).contains("Service capabilities", "Supported dependencies")
.doesNotContain("Examples:", "curl");
}
@Test
void missingDependencyProperException() {
try {
@ -375,12 +169,6 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
}
}
@Test
void homeIsJson() {
String body = invokeHome(null, (String[]) null).getBody();
assertThat(body).contains("\"dependencies\"");
}
@Test
void webIsAddedPom() {
String body = getRestTemplate().getForObject(createUrl("/pom.xml?packaging=war"), String.class);
@ -403,18 +191,15 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
}
@Test
void installer() {
ResponseEntity<String> response = getRestTemplate().getForEntity(createUrl("install.sh"), String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
void curlCanStillDownloadZipArchive() {
ResponseEntity<byte[]> response = execute("/starter.zip", byte[].class, "curl/1.2.4", "*/*");
assertDefaultProject(projectFromArchive(response.getBody()));
}
private String getMetadataJson() {
return getMetadataJson(null);
}
private String getMetadataJson(String userAgentHeader, String... acceptHeaders) {
return invokeHome(userAgentHeader, acceptHeaders).getBody();
@Test
void curlCanStillDownloadTgzArchive() {
ResponseEntity<byte[]> response = execute("/starter.tgz", byte[].class, "curl/1.2.4", "*/*");
assertDefaultProject(tgzProjectAssert(response.getBody()));
}
private static void assertStandardErrorBody(String body, String message) {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.spring.initializr.web.project;
package io.spring.initializr.web.controller;
import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.InitializrMetadataBuilder;
@ -28,6 +28,7 @@ import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@ -37,10 +38,12 @@ import org.springframework.web.client.HttpClientErrorException;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link ProjectMetadataController} on a real http server.
*
* @author Stephane Nicoll
*/
@ActiveProfiles("test-default")
class MainControllerServiceMetadataIntegrationTests extends AbstractFullStackInitializrIntegrationTests {
class ProjectMetadataControllerCustomDefaultsIntegrationTests extends AbstractFullStackInitializrIntegrationTests {
@Autowired
private InitializrMetadataProvider metadataProvider;
@ -84,4 +87,26 @@ class MainControllerServiceMetadataIntegrationTests extends AbstractFullStackIni
validateCurrentMetadata(response);
}
@Test
void noBootVersion() throws JSONException {
ResponseEntity<String> response = execute("/dependencies", String.class, null, "application/json");
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE);
validateDependenciesOutput("2.1.4", response.getBody());
}
@Test
void filteredDependencies() throws JSONException {
ResponseEntity<String> response = execute("/dependencies?bootVersion=2.2.1.RELEASE", String.class, null,
"application/json");
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE);
validateDependenciesOutput("2.2.1", response.getBody());
}
protected void validateDependenciesOutput(String version, String actual) throws JSONException {
JSONObject expected = readJsonFrom("metadata/dependencies/test-dependencies-" + version + ".json");
JSONAssert.assertEquals(expected, new JSONObject(actual), JSONCompareMode.STRICT);
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web.controller;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
import io.spring.initializr.web.AbstractInitializrIntegrationTests;
import io.spring.initializr.web.mapper.InitializrMetadataVersion;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.client.HttpClientErrorException;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link ProjectMetadataController}.
*
* @author Stephane Nicoll
*/
@ActiveProfiles("test-default")
public class ProjectMetadataControllerIntegrationTests extends AbstractInitializrControllerIntegrationTests {
@Test
void metadataWithNoAcceptHeader() {
// rest template sets application/json by default
ResponseEntity<String> response = invokeHome(null, "*/*");
validateCurrentMetadata(response);
}
@Test
@Disabled("Need a comparator that does not care about the number of elements in an array")
void currentMetadataCompatibleWithV2() {
ResponseEntity<String> response = invokeHome(null, "*/*");
validateMetadata(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE, "2.0.0",
JSONCompareMode.LENIENT);
}
@Test
void metadataWithV2AcceptHeader() {
ResponseEntity<String> response = invokeHome(null, "application/vnd.initializr.v2+json");
validateMetadata(response, InitializrMetadataVersion.V2.getMediaType(), "2.0.0", JSONCompareMode.STRICT);
}
@Test
void metadataWithCurrentAcceptHeader() {
getRequests().setFields("_links.maven-project", "dependencies.values[0]", "type.values[0]",
"javaVersion.values[0]", "packaging.values[0]", "bootVersion.values[0]", "language.values[0]");
ResponseEntity<String> response = invokeHome(null, "application/vnd.initializr.v2.1+json");
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void metadataWithSeveralAcceptHeader() {
ResponseEntity<String> response = invokeHome(null, "application/vnd.initializr.v2.1+json",
"application/vnd.initializr.v2+json");
validateContentType(response, AbstractInitializrIntegrationTests.CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void metadataWithHalAcceptHeader() {
ResponseEntity<String> response = invokeHome(null, "application/hal+json");
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
validateContentType(response, ProjectMetadataController.HAL_JSON_CONTENT_TYPE);
validateCurrentMetadata(response.getBody());
}
@Test
void metadataWithUnknownAcceptHeader() {
try {
invokeHome(null, "application/vnd.initializr.v5.4+json");
}
catch (HttpClientErrorException ex) {
assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.NOT_ACCEPTABLE);
}
}
@Test
void homeIsJson() {
String body = invokeHome(null, (String[]) null).getBody();
assertThat(body).contains("\"dependencies\"");
}
@Test
void unknownAgentReceivesJsonByDefault() {
ResponseEntity<String> response = invokeHome("foo/1.0", "*/*");
validateCurrentMetadata(response);
}
@Test
// Test that the current output is exactly what we expect
void validateCurrentProjectMetadata() {
validateCurrentMetadata(getMetadataJson());
}
private String getMetadataJson() {
return getMetadataJson(null);
}
private String getMetadataJson(String userAgentHeader, String... acceptHeaders) {
return invokeHome(userAgentHeader, acceptHeaders).getBody();
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.spring.initializr.web.project;
package io.spring.initializr.web.controller;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
import io.spring.initializr.web.mapper.InitializrMetadataVersion;
@ -24,23 +24,13 @@ import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests with {@code forceSsl} enabled.
* Integration tests for {@link ProjectMetadataController} with {@code forceSsl} enabled.
*
* @author Stephane Nicoll
*/
@ActiveProfiles({ "test-default", "test-ssl" })
class MainControllerSslIntegrationTests extends AbstractInitializrControllerIntegrationTests {
@Test
void forceSsl() {
ResponseEntity<String> response = invokeHome("curl/1.2.4", "*/*");
String body = response.getBody();
assertThat(body).as("Must force https").contains("https://start.spring.io/");
assertThat(body).as("Must force https").doesNotContain("http://");
}
class ProjectMetadataControllerSslIntegrationTests extends AbstractInitializrControllerIntegrationTests {
@Test
void forceSslInMetadata() {

View File

@ -0,0 +1,47 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web.controller;
import java.net.URI;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link SpringCliDistributionController} with custom defaults.
*
* @author Stephane Nicoll
*/
@ActiveProfiles({ "test-default", "test-custom-env" })
public class SpringCliDistributionControllerCustomEnvsIntegrationTests
extends AbstractInitializrControllerIntegrationTests {
@Test
void downloadCliWithCustomRepository() throws Exception {
ResponseEntity<?> entity = getRestTemplate().getForEntity(createUrl("/spring"), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
String expected = "https://repo.spring.io/lib-release/org/springframework/boot/spring-boot-cli/2.1.4.RELEASE/spring-boot-cli-2.1.4.RELEASE-bin.zip";
assertThat(entity.getHeaders().getLocation()).isEqualTo(new URI(expected));
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web.controller;
import java.net.URI;
import java.net.URISyntaxException;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link SpringCliDistributionController}.
*
* @author Stephane Nicoll
*/
@ActiveProfiles("test-default")
public class SpringCliDistributionControllerIntegrationTests extends AbstractInitializrControllerIntegrationTests {
@Test
void downloadCli() throws Exception {
assertSpringCliRedirect("/spring", "zip");
}
@Test
void downloadCliAsZip() throws Exception {
assertSpringCliRedirect("/spring.zip", "zip");
}
@Test
void downloadCliAsTarGz() throws Exception {
assertSpringCliRedirect("/spring.tar.gz", "tar.gz");
}
@Test
void downloadCliAsTgz() throws Exception {
assertSpringCliRedirect("/spring.tgz", "tar.gz");
}
@Test
void installer() {
ResponseEntity<String> response = getRestTemplate().getForEntity(createUrl("install.sh"), String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
}
private void assertSpringCliRedirect(String context, String extension) throws URISyntaxException {
ResponseEntity<?> entity = getRestTemplate().getForEntity(createUrl(context), ResponseEntity.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FOUND);
String expected = "https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/2.1.4.RELEASE/spring-boot-cli-2.1.4.RELEASE-bin."
+ extension;
assertThat(entity.getHeaders().getLocation()).isEqualTo(new URI(expected));
}
}

View File

@ -1,60 +0,0 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web.project;
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Stephane Nicoll
*/
@ActiveProfiles("test-default")
class MainControllerDependenciesTests extends AbstractInitializrControllerIntegrationTests {
@Test
void noBootVersion() throws JSONException {
ResponseEntity<String> response = execute("/dependencies", String.class, null, "application/json");
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE);
validateDependenciesOutput("2.1.4", response.getBody());
}
@Test
void filteredDependencies() throws JSONException {
ResponseEntity<String> response = execute("/dependencies?bootVersion=2.2.1.RELEASE", String.class, null,
"application/json");
assertThat(response.getHeaders().getFirst(HttpHeaders.ETAG)).isNotNull();
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE);
validateDependenciesOutput("2.2.1", response.getBody());
}
protected void validateDependenciesOutput(String version, String actual) throws JSONException {
JSONObject expected = readJsonFrom("metadata/dependencies/test-dependencies-" + version + ".json");
JSONAssert.assertEquals(expected, new JSONObject(actual), JSONCompareMode.STRICT);
}
}