Add platform compatibility range support

This commit adds a new `platformCompatibilityRange` in the metadata that
can be used to restrict the valid platform versions. If a project is
requested or metadata needs to be resolved against a version that does
not match the range, an exception is thrown.

Closes gh-1048
This commit is contained in:
Stephane Nicoll
2020-01-03 14:08:37 +01:00
parent e25fb74d23
commit eef529aa7c
9 changed files with 119 additions and 20 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletResponse;
import io.spring.initializr.generator.version.Version;
import io.spring.initializr.metadata.DependencyMetadata;
import io.spring.initializr.metadata.DependencyMetadataProvider;
import io.spring.initializr.metadata.InitializrConfiguration.Env;
import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import io.spring.initializr.metadata.InvalidInitializrMetadataException;
@@ -32,6 +33,7 @@ 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.project.InvalidProjectRequestException;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpStatus;
@@ -97,6 +99,12 @@ public class ProjectMetadataController extends AbstractMetadataController {
response.sendError(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
}
@ExceptionHandler
public void invalidProjectRequest(HttpServletResponse response, InvalidProjectRequestException ex)
throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
}
/**
* Return the {@link CacheControl} response headers to use for the specified
* {@link InitializrMetadata metadata}. If no cache should be applied
@@ -112,6 +120,11 @@ public class ProjectMetadataController extends AbstractMetadataController {
InitializrMetadata metadata = this.metadataProvider.get();
Version v = (bootVersion != null) ? Version.parse(bootVersion)
: Version.parse(metadata.getBootVersions().getDefault().getId());
Env env = metadata.getConfiguration().getEnv();
if (!env.isCompatiblePlatformVersion(v)) {
throw new InvalidProjectRequestException("Invalid Spring Boot version '" + bootVersion
+ "', Spring Boot compatibility range is " + env.determinePlatformCompatibilityRangeRequirement());
}
DependencyMetadata dependencyMetadata = this.dependencyMetadataProvider.get(metadata, v);
String content = new DependencyMetadataV21JsonMapper().write(dependencyMetadata);
return ResponseEntity.ok().contentType(version.getMediaType()).eTag(createUniqueId(content))

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@@ -27,6 +27,7 @@ import io.spring.initializr.generator.project.ProjectDescription;
import io.spring.initializr.generator.version.Version;
import io.spring.initializr.metadata.DefaultMetadataElement;
import io.spring.initializr.metadata.Dependency;
import io.spring.initializr.metadata.InitializrConfiguration.Env;
import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.Type;
import io.spring.initializr.metadata.support.MetadataBuildItemMapper;
@@ -42,8 +43,6 @@ import io.spring.initializr.metadata.support.MetadataBuildItemMapper;
public class DefaultProjectRequestToDescriptionConverter
implements ProjectRequestToDescriptionConverter<ProjectRequest> {
private static final Version VERSION_1_5_0 = Version.parse("1.5.0.RELEASE");
@Override
public ProjectDescription convert(ProjectRequest request, InitializrMetadata metadata) {
MutableProjectDescription description = new MutableProjectDescription();
@@ -82,18 +81,19 @@ public class DefaultProjectRequestToDescriptionConverter
}
private void validate(ProjectRequest request, InitializrMetadata metadata) {
validateSpringBootVersion(request);
validateSpringBootVersion(request, metadata);
validateType(request.getType(), metadata);
validateLanguage(request.getLanguage(), metadata);
validatePackaging(request.getPackaging(), metadata);
validateDependencies(request, metadata);
}
private void validateSpringBootVersion(ProjectRequest request) {
private void validateSpringBootVersion(ProjectRequest request, InitializrMetadata metadata) {
Version bootVersion = Version.safeParse(request.getBootVersion());
if (bootVersion != null && bootVersion.compareTo(VERSION_1_5_0) < 0) {
throw new InvalidProjectRequestException(
"Invalid Spring Boot version " + bootVersion + " must be 1.5.0 or higher");
Env env = metadata.getConfiguration().getEnv();
if (bootVersion != null && !env.isCompatiblePlatformVersion(bootVersion)) {
throw new InvalidProjectRequestException("Invalid Spring Boot version '" + bootVersion
+ "', Spring Boot compatibility range is " + env.determinePlatformCompatibilityRangeRequirement());
}
}

View File

@@ -21,7 +21,9 @@ 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.test.context.ActiveProfiles;
import org.springframework.web.client.HttpClientErrorException;
import static org.assertj.core.api.Assertions.assertThat;
@@ -46,4 +48,29 @@ class ProjectGenerationControllerCustomEnvIntegrationTests extends AbstractIniti
.hasDependency(Dependency.createSpringBootStarter("test", Dependency.SCOPE_TEST));
}
@Test
void generateProjectWithUnsupportedPlatformVersion() {
try {
execute("/starter.zip?bootVersion=1.5.12.RELEASE", byte[].class, null, (String[]) null);
}
catch (HttpClientErrorException ex) {
assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
assertThat(ex.getMessage()).contains("Invalid Spring Boot version",
"Spring Boot compatibility range is >=2.0.0.RELEASE");
}
}
@Test
void getDependenciesMetadataWithUnsupportedPlatformVersion() {
try {
execute("/dependencies?bootVersion=1.5.12.RELEASE", String.class, "application/vnd.initializr.v2.1+json",
"application/json");
}
catch (HttpClientErrorException ex) {
assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
assertThat(ex.getMessage()).contains("Invalid Spring Boot version",
"Spring Boot compatibility range is >=2.0.0.RELEASE");
}
}
}

View File

@@ -66,12 +66,23 @@ class DefaultProjectRequestToDescriptionConverterTests {
}
@Test
void convertWhenSpringBootVersionInvalidShouldThrowException() {
void convertWhenPlatformCompatiblityRangeIsNotSetShouldNotThrowException() {
this.metadata = InitializrMetadataTestBuilder.withDefaults().setPlatformCompatibilityRange(null).build();
ProjectRequest request = createProjectRequest();
request.setBootVersion("1.2.3.M4");
request.setBootVersion("1.5.9.RELEASE");
assertThat(this.converter.convert(request, this.metadata).getPlatformVersion())
.isEqualTo(Version.parse("1.5.9.RELEASE"));
}
@Test
void convertWhenSpringBootVersionInvalidShouldThrowException() {
this.metadata = InitializrMetadataTestBuilder.withDefaults()
.setPlatformCompatibilityRange("[2.0.0.RELEASE,2.3.0.M1)").build();
ProjectRequest request = createProjectRequest();
request.setBootVersion("1.5.9.RELEASE");
assertThatExceptionOfType(InvalidProjectRequestException.class)
.isThrownBy(() -> this.converter.convert(request, this.metadata))
.withMessage("Invalid Spring Boot version 1.2.3.M4 must be 1.5.0 or higher");
.isThrownBy(() -> this.converter.convert(request, this.metadata)).withMessage(
"Invalid Spring Boot version '1.5.9.RELEASE', Spring Boot compatibility range is >=2.0.0.RELEASE and <2.3.0.M1");
}
@Test

View File

@@ -5,5 +5,6 @@ initializr:
fallbackApplicationName: FooBarApplication
invalidApplicationNames:
- InvalidApplication
platform-compatibility-range: "2.0.0.RELEASE"
kotlin:
default-version: 1.0.0-beta-2423

View File

@@ -33,6 +33,7 @@
"artifactRepository": "https://repo.spring.io/release/",
"fallbackApplicationName": "Application",
"forceSsl": false,
"platformCompatibilityRange": null,
"gradle": {
"dependencyManagementPluginVersion": "1.0.0.RELEASE"
},