Improve dependency mapping to include coordinates

This commit improves the dependency mapping infrastructure to include the
groupId and artifactId attributes alongside the existing version. For
consistency, the attribute has been renamed from `versions` to
`mappings`.

This allows to better support the `redis` use case: the starter was
renamed from `spring-boot-starter-redis` to
`spring-boot-starter-data-redis` and previously we had two entries
excluding each other using version ranges.

Closes gh-219
This commit is contained in:
Stephane Nicoll 2016-04-24 05:20:59 +02:00
parent b0f1abed41
commit 2d9ac9bd5a
5 changed files with 83 additions and 34 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@ -62,10 +62,10 @@ class Dependency extends MetadataElement {
String version
/**
* Versions mapping if the version differs according to the Spring Boot
* version. If no mapping matches, {@code version} is used.
* Dependency mapping if an attribute of the dependency differs according to the
* Spring Boot version. If no mapping matches, default attributes are used.
*/
List<Mapping> versions = []
List<Mapping> mappings = []
String scope = SCOPE_COMPILE
@ -154,7 +154,7 @@ class Dependency extends MetadataElement {
"dependency with id '$id'")
}
}
versions.each {
mappings.each {
try {
it.range = VersionRange.parse(it.versionRange)
} catch (InvalidVersionException ex) {
@ -168,11 +168,13 @@ class Dependency extends MetadataElement {
* a {@link Dependency} instance that has its state resolved against the specified version.
*/
Dependency resolve(Version bootVersion) {
for (Mapping mapping : versions) {
for (Mapping mapping : mappings) {
if (mapping.range.match(bootVersion)) {
def dependency = new Dependency(this)
dependency.groupId = mapping.groupId ? mapping.groupId : this.groupId
dependency.artifactId = mapping.artifactId ? mapping.artifactId : this.artifactId
dependency.version = mapping.version ? mapping.version : this.version
dependency.versions = null
dependency.mappings = null
return dependency
}
}
@ -202,10 +204,29 @@ class Dependency extends MetadataElement {
id = sb.toString()
}
/**
* Map several attribute of the dependency for a given version range.
*/
static class Mapping {
/**
* The version range of this mapping.
*/
String versionRange
/**
* The version to use for this mapping or {@code null} to use the default.
*/
String groupId;
/**
* The groupId to use for this mapping or {@code null} to use the default.
*/
String artifactId;
/**
* The artifactId to use for this mapping or {@code null} to use the default.
*/
String version
private VersionRange range

View File

@ -158,9 +158,9 @@ class ProjectRequestTests {
@Test
void resolveDependencyVersion() {
def dependency = createDependency('org.foo', 'bar', '1.2.0.RELEASE')
dependency.versions << new Dependency.Mapping(
dependency.mappings << new Dependency.Mapping(
version: '0.1.0.RELEASE', versionRange: '[1.0.0.RELEASE, 1.1.0.RELEASE)')
dependency.versions << new Dependency.Mapping(
dependency.mappings << new Dependency.Mapping(
version: '0.2.0.RELEASE', versionRange: '1.1.0.RELEASE')
def metadata = InitializrMetadataTestBuilder.withDefaults()
.addDependencyGroup('code', dependency).build()

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@ -148,7 +148,7 @@ class DependencyTests {
@Test
void resolveInvalidMapping() {
def dependency = new Dependency(id: 'web')
dependency.versions << new Dependency.Mapping(
dependency.mappings << new Dependency.Mapping(
versionRange: 'foo-bar', version: '0.1.0.RELEASE')
thrown.expect(InvalidInitializrMetadataException)
thrown.expectMessage('foo-bar')
@ -156,25 +156,47 @@ class DependencyTests {
}
@Test
void resolveMatchingMapping() {
void resolveMatchingVersionMapping() {
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(
keywords: ['foo', 'bar'], aliases: ['the-web'], facets: ['web'])
dependency.mappings << new Dependency.Mapping(
versionRange: '[1.1.0.RELEASE, 1.2.0.RELEASE)', version: '0.1.0.RELEASE')
dependency.versions << new Dependency.Mapping(
dependency.mappings << 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
validateResolvedWebDependency(dependency.resolve(Version.parse('1.1.5.RELEASE')),
'org.springframework.boot', 'spring-boot-starter-web', '0.1.0.RELEASE')
validateResolvedWebDependency(dependency.resolve(Version.parse('1.2.0.RELEASE')),
'org.springframework.boot', 'spring-boot-starter-web', '0.2.0.RELEASE')
validateResolvedWebDependency(dependency.resolve(Version.parse('2.1.3.M1')),
'org.springframework.boot', 'spring-boot-starter-web', '0.3.0.RELEASE') // default
}
static void validateResolvedWebDependency(def dependency, def expectedVersion) {
@Test
void resolveMatchArtifactMapping() {
def dependency = new Dependency(id: 'web', description: 'A web dependency', version: '0.3.0.RELEASE',
keywords: ['foo', 'bar'], aliases: ['the-web'], facets: ['web'])
dependency.mappings << new Dependency.Mapping(
versionRange: '[1.1.0.RELEASE, 1.2.0.RELEASE)', groupId: 'org.spring.boot')
dependency.mappings << new Dependency.Mapping(
versionRange: '[1.2.0.RELEASE, 1.3.0.RELEASE)', artifactId: 'starter-web')
dependency.resolve()
validateResolvedWebDependency(dependency.resolve(Version.parse('1.1.5.RELEASE')),
'org.spring.boot', 'spring-boot-starter-web', '0.3.0.RELEASE')
validateResolvedWebDependency(dependency.resolve(Version.parse('1.2.0.RELEASE')),
'org.springframework.boot', 'starter-web', '0.3.0.RELEASE')
validateResolvedWebDependency(dependency.resolve(Version.parse('2.1.3.M1')),
'org.springframework.boot', 'spring-boot-starter-web', '0.3.0.RELEASE') // default
}
static void validateResolvedWebDependency(
def dependency, def expectedGroupId, def expectedArtifactId, def expectedVersion) {
assertEquals expectedVersion, dependency.version
assertEquals 'web', dependency.id
assertEquals 'org.springframework.boot', dependency.groupId
assertEquals 'spring-boot-starter-web', dependency.artifactId
assertEquals expectedGroupId, dependency.groupId
assertEquals expectedArtifactId, dependency.artifactId
assertEquals 2, dependency.keywords.size()
assertEquals 1, dependency.aliases.size()
assertEquals 1, dependency.facets.size()

View File

@ -91,7 +91,7 @@ initializr:
groupId: org.projectlombok
artifactId: lombok
description: Java annotation library which help to reduce boilerplate code and code faster
versions:
mappings:
- versionRange: "[1.2.0.RELEASE,1.4.0.M1)"
version: 1.16.6
starter: false
@ -153,7 +153,7 @@ initializr:
description: Document RESTful services by combining hand-written and auto-generated documentation
groupId: org.springframework.restdocs
artifactId: spring-restdocs-mockmvc
versions:
mappings:
- versionRange: "[1.2.0.RELEASE,1.3.0.RC1)"
version: 1.0.1.RELEASE
scope: test
@ -243,7 +243,7 @@ initializr:
description: PostgreSQL jdbc driver
groupId: org.postgresql
artifactId: postgresql
versions:
mappings:
- versionRange: "[1.2.0.RELEASE,1.3.0.M1)"
version: 9.4-1201-jdbc41
scope: runtime
@ -269,11 +269,13 @@ initializr:
- name: Redis
id: data-redis
description: REDIS key-value data store, including spring-redis
versionRange: 1.4.0.M1
- name: Redis
id: redis
description: REDIS key-value data store, including spring-redis
versionRange: "[1.1.5.RELEASE,1.4.0.M1)"
aliases:
- redis
mappings:
- versionRange: 1.4.0.M1
artifactId: spring-boot-starter-data-redis
- versionRange: "[1.1.5.RELEASE,1.4.0.M1)"
artifactId: spring-boot-starter-redis
- name: Gemfire
id: data-gemfire
description: GemFire distributed data store including spring-data-gemfire

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@ -54,20 +54,24 @@ class DefaultDependencyMetadataProviderTests {
@Test
void resolveDependencies() {
def first = new Dependency(id: 'first', groupId: 'org.foo', artifactId: 'first')
first.versions << new Dependency.Mapping(versionRange: '[1.0.0.RELEASE, 1.1.0.RELEASE)',
version: '0.1.0.RELEASE')
first.versions << new Dependency.Mapping(versionRange: '1.1.0.RELEASE',
version: '0.2.0.RELEASE')
first.mappings << new Dependency.Mapping(versionRange: '[1.0.0.RELEASE, 1.1.0.RELEASE)',
version: '0.1.0.RELEASE', groupId: 'org.bar', artifactId: 'second')
first.mappings << new Dependency.Mapping(versionRange: '1.1.0.RELEASE',
version: '0.2.0.RELEASE', groupId: 'org.biz', artifactId: 'third')
def second = new Dependency(id: 'second', groupId: 'org.foo', artifactId: 'second')
def metadata = InitializrMetadataTestBuilder.withDefaults()
.addDependencyGroup('test', first, second).build()
def dependencyMetadata = provider.get(metadata, Version.parse('1.0.5.RELEASE'))
assertEquals 2, dependencyMetadata.dependencies.size()
assertEquals('org.bar', dependencyMetadata.dependencies['first'].groupId)
assertEquals('second', dependencyMetadata.dependencies['first'].artifactId)
assertEquals('0.1.0.RELEASE', dependencyMetadata.dependencies['first'].version)
def anotherDependencyMetadata = provider.get(metadata, Version.parse('1.1.0.RELEASE'))
assertEquals 2, anotherDependencyMetadata.dependencies.size()
assertEquals('org.biz', anotherDependencyMetadata.dependencies['first'].groupId)
assertEquals('third', anotherDependencyMetadata.dependencies['first'].artifactId)
assertEquals('0.2.0.RELEASE', anotherDependencyMetadata.dependencies['first'].version)
}