mirror of
https://gitee.com/dcren/initializr.git
synced 2026-02-25 21:22:58 +08:00
Dependency versions mapping support
If a dependency is fist managed by the service and later on by Spring Boot, there is no way to keep a single entry that would change the dependency according to the Spring Boot version. A hack would consist of creating two entries that would have an exclusive range but it's far from ideal. This commit adds a `versions` attribute on each dependency that can define an arbitrary number of mappings between a version and a range. If one of those range matches, the related version is used (and the absence of version means that no version need to be included). If no mapping matched (or if there are no mappings at all), the default version is used. Closes gh-168
This commit is contained in:
@@ -84,6 +84,8 @@ class ProjectRequest {
|
||||
*/
|
||||
void resolve(InitializrMetadata metadata) {
|
||||
List<String> depIds = style ? style : dependencies
|
||||
String actualBootVersion = bootVersion ?: metadata.bootVersions.default.id
|
||||
Version requestedVersion = Version.parse(actualBootVersion)
|
||||
resolvedDependencies = depIds.collect {
|
||||
def dependency = metadata.dependencies.get(it)
|
||||
if (dependency == null) {
|
||||
@@ -94,10 +96,8 @@ class ProjectRequest {
|
||||
dependency = new Dependency()
|
||||
dependency.asSpringBootStarter(it)
|
||||
}
|
||||
dependency
|
||||
dependency.resolve(requestedVersion)
|
||||
}
|
||||
String actualBootVersion = bootVersion ?: metadata.bootVersions.default.id
|
||||
Version requestedVersion = Version.parse(actualBootVersion)
|
||||
Set<String> bomIds = []
|
||||
resolvedDependencies.each {
|
||||
it.facets.each {
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
package io.spring.initializr.metadata
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import groovy.transform.AutoClone
|
||||
import groovy.transform.AutoCloneStyle
|
||||
import groovy.transform.ToString
|
||||
import io.spring.initializr.util.InvalidVersionException
|
||||
import io.spring.initializr.util.Version
|
||||
import io.spring.initializr.util.VersionRange
|
||||
|
||||
/**
|
||||
@@ -29,7 +32,8 @@ import io.spring.initializr.util.VersionRange
|
||||
* @since 1.0
|
||||
*/
|
||||
@ToString(ignoreNulls = true, includePackage = false)
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@AutoClone(style = AutoCloneStyle.COPY_CONSTRUCTOR)
|
||||
class Dependency extends MetadataElement {
|
||||
|
||||
static final String SCOPE_COMPILE = 'compile'
|
||||
@@ -51,8 +55,18 @@ class Dependency extends MetadataElement {
|
||||
|
||||
String artifactId
|
||||
|
||||
/**
|
||||
* The default version, can be {@code null} to indicate that the
|
||||
* version is managed by the project and does not need to be specified.
|
||||
*/
|
||||
String version
|
||||
|
||||
/**
|
||||
* Versions mapping if the version differs according to the Spring Boot
|
||||
* version. If no mapping matches, {@code version} is used.
|
||||
*/
|
||||
List<Mapping> versions = []
|
||||
|
||||
String scope = SCOPE_COMPILE
|
||||
|
||||
String description
|
||||
@@ -140,6 +154,29 @@ class Dependency extends MetadataElement {
|
||||
"dependency with id '$id'")
|
||||
}
|
||||
}
|
||||
versions.each {
|
||||
try {
|
||||
it.range = VersionRange.parse(it.versionRange)
|
||||
} catch (InvalidVersionException ex) {
|
||||
throw new InvalidInitializrMetadataException("Invalid version range $it.versionRange for $this", ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve this instance according to the specified Spring Boot {@link Version}. Return
|
||||
* a {@link Dependency} instance that has its state resolved against the specified version.
|
||||
*/
|
||||
Dependency resolve(Version bootVersion) {
|
||||
for (Mapping mapping : versions) {
|
||||
if (mapping.range.match(bootVersion)) {
|
||||
def dependency = new Dependency(this)
|
||||
dependency.version = mapping.version ? mapping.version : this.version
|
||||
dependency.versions = null
|
||||
return dependency
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,4 +192,14 @@ class Dependency extends MetadataElement {
|
||||
id = sb.toString()
|
||||
}
|
||||
|
||||
static class Mapping {
|
||||
|
||||
String versionRange
|
||||
|
||||
String version
|
||||
|
||||
private VersionRange range
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,12 +16,16 @@
|
||||
|
||||
package io.spring.initializr.metadata
|
||||
|
||||
import groovy.transform.AutoClone
|
||||
import groovy.transform.AutoCloneStyle
|
||||
|
||||
/**
|
||||
* A basic meta-data element
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.0
|
||||
*/
|
||||
@AutoClone(style = AutoCloneStyle.COPY_CONSTRUCTOR)
|
||||
class MetadataElement {
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,7 +116,7 @@ class ProjectRequestTests {
|
||||
def metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addDependencyGroup('code', 'org.foo:bar').build()
|
||||
|
||||
request.style << 'org.foo:acme' // does not exist and
|
||||
request.style << 'org.foo:acme' // does not exist
|
||||
|
||||
thrown.expect(InvalidProjectRequestException)
|
||||
thrown.expectMessage('org.foo:acme')
|
||||
@@ -154,6 +154,29 @@ class ProjectRequestTests {
|
||||
request.resolve(metadata)
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDependencyVersion() {
|
||||
def dependency = createDependency('org.foo', 'bar', '1.2.0.RELEASE')
|
||||
dependency.versions << new Dependency.Mapping(
|
||||
version: '0.1.0.RELEASE', versionRange: '[1.0.0.RELEASE, 1.1.0.RELEASE)')
|
||||
dependency.versions << new Dependency.Mapping(
|
||||
version: '0.2.0.RELEASE', versionRange: '1.1.0.RELEASE')
|
||||
def metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addDependencyGroup('code', dependency).build()
|
||||
|
||||
def request = new ProjectRequest()
|
||||
request.bootVersion = '1.0.5.RELEASE'
|
||||
request.style << 'org.foo:bar'
|
||||
request.resolve(metadata)
|
||||
assertDependency(request.resolvedDependencies[0], 'org.foo', 'bar', '0.1.0.RELEASE')
|
||||
|
||||
def anotherRequest = new ProjectRequest()
|
||||
anotherRequest.bootVersion = '1.1.0.RELEASE'
|
||||
anotherRequest.style << 'org.foo:bar'
|
||||
anotherRequest.resolve(metadata)
|
||||
assertDependency(anotherRequest.resolvedDependencies[0], 'org.foo', 'bar', '0.2.0.RELEASE')
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveBuild() {
|
||||
def request = new ProjectRequest()
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package io.spring.initializr.metadata
|
||||
|
||||
import io.spring.initializr.util.Version
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.ExpectedException
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertNull
|
||||
import static org.junit.Assert.assertSame
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
@@ -136,4 +138,47 @@ class DependencyTests {
|
||||
dependency.generateId()
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveNoMapping() {
|
||||
def dependency = new Dependency(id: 'web')
|
||||
dependency.resolve()
|
||||
assertSame dependency, dependency.resolve(Version.parse('1.2.0.RELEASE'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveInvalidMapping() {
|
||||
def dependency = new Dependency(id: 'web')
|
||||
dependency.versions << new Dependency.Mapping(
|
||||
versionRange: 'foo-bar', version: '0.1.0.RELEASE')
|
||||
thrown.expect(InvalidInitializrMetadataException)
|
||||
thrown.expectMessage('foo-bar')
|
||||
dependency.resolve()
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveMatchingMapping() {
|
||||
def dependency = new Dependency(id: 'web', description: 'A web dependency', version: '0.3.0.RELEASE',
|
||||
keywords: ['foo', 'bar'], aliases: ['the-web'], facets: ['web'] )
|
||||
dependency.versions << new Dependency.Mapping(
|
||||
versionRange: '[1.1.0.RELEASE, 1.2.0.RELEASE)', version: '0.1.0.RELEASE')
|
||||
dependency.versions << new Dependency.Mapping(
|
||||
versionRange: '[1.2.0.RELEASE, 1.3.0.RELEASE)', version: '0.2.0.RELEASE')
|
||||
dependency.resolve()
|
||||
|
||||
validateResolvedWebDependency(dependency.resolve(Version.parse('1.1.5.RELEASE')), '0.1.0.RELEASE')
|
||||
validateResolvedWebDependency(dependency.resolve(Version.parse('1.2.0.RELEASE')), '0.2.0.RELEASE')
|
||||
validateResolvedWebDependency(dependency.resolve(Version.parse('2.1.3.M1')), '0.3.0.RELEASE') // default
|
||||
}
|
||||
|
||||
static void validateResolvedWebDependency(def dependency, def expectedVersion) {
|
||||
assertEquals expectedVersion, dependency.version
|
||||
assertEquals 'web', dependency.id
|
||||
assertEquals 'org.springframework.boot', dependency.groupId
|
||||
assertEquals 'spring-boot-starter-web', dependency.artifactId
|
||||
assertEquals 2, dependency.keywords.size()
|
||||
assertEquals 1, dependency.aliases.size()
|
||||
assertEquals 1, dependency.facets.size()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,9 +57,6 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"aliases": [],
|
||||
"keywords": [],
|
||||
"weight": 0,
|
||||
"starter": true,
|
||||
"artifactId": "spring-boot-starter-web",
|
||||
"description": "Web dependency description",
|
||||
@@ -70,12 +67,8 @@
|
||||
"scope": "compile"
|
||||
},
|
||||
{
|
||||
"aliases": [],
|
||||
"keywords": [],
|
||||
"weight": 0,
|
||||
"starter": true,
|
||||
"artifactId": "spring-boot-starter-security",
|
||||
"facets": [],
|
||||
"groupId": "org.springframework.boot",
|
||||
"id": "security",
|
||||
"name": "Security",
|
||||
@@ -83,11 +76,8 @@
|
||||
},
|
||||
{
|
||||
"aliases": ["jpa"],
|
||||
"keywords": [],
|
||||
"weight": 0,
|
||||
"starter": true,
|
||||
"artifactId": "spring-boot-starter-data-jpa",
|
||||
"facets": [],
|
||||
"groupId": "org.springframework.boot",
|
||||
"id": "data-jpa",
|
||||
"name": "Data JPA",
|
||||
@@ -99,9 +89,7 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"aliases": [],
|
||||
"artifactId": "foo",
|
||||
"facets": [],
|
||||
"groupId": "org.acme",
|
||||
"id": "org.acme:foo",
|
||||
"name": "Foo",
|
||||
@@ -112,12 +100,8 @@
|
||||
"version": "1.3.5"
|
||||
},
|
||||
{
|
||||
"aliases": [],
|
||||
"keywords": [],
|
||||
"weight": 0,
|
||||
"starter": true,
|
||||
"artifactId": "bar",
|
||||
"facets": [],
|
||||
"groupId": "org.acme",
|
||||
"id": "org.acme:bar",
|
||||
"name": "Bar",
|
||||
@@ -125,12 +109,8 @@
|
||||
"version": "2.1.0"
|
||||
},
|
||||
{
|
||||
"aliases": [],
|
||||
"keywords": [],
|
||||
"weight": 0,
|
||||
"starter": true,
|
||||
"artifactId": "biz",
|
||||
"facets": [],
|
||||
"groupId": "org.acme",
|
||||
"id": "org.acme:biz",
|
||||
"name": "Biz",
|
||||
@@ -139,12 +119,8 @@
|
||||
"versionRange": "1.2.0.BUILD-SNAPSHOT"
|
||||
},
|
||||
{
|
||||
"aliases": [],
|
||||
"keywords": [],
|
||||
"weight": 0,
|
||||
"starter": true,
|
||||
"artifactId": "bur",
|
||||
"facets": [],
|
||||
"groupId": "org.acme",
|
||||
"id": "org.acme:bur",
|
||||
"name": "Bur",
|
||||
@@ -153,12 +129,8 @@
|
||||
"versionRange": "[1.1.4.RELEASE,1.2.0.BUILD-SNAPSHOT)"
|
||||
},
|
||||
{
|
||||
"aliases": [],
|
||||
"keywords": [],
|
||||
"weight": 0,
|
||||
"starter": true,
|
||||
"artifactId": "my-api",
|
||||
"facets": [],
|
||||
"groupId": "org.acme",
|
||||
"id": "my-api",
|
||||
"name": "My API",
|
||||
|
||||
Reference in New Issue
Block a user