Add dependency scope support

Add an extra 'scope' attribute to any dependency (defaults to 'compile').

Update maven and gradle build templates to support compile, runtime,
provided and test dependencies. Also, dependencies are now sorted
according to their ids.

Closes gh-83
This commit is contained in:
Stephane Nicoll
2015-02-24 15:54:50 +01:00
parent 89536363f4
commit bf8289b570
16 changed files with 173 additions and 34 deletions

View File

@@ -7,6 +7,7 @@ order.
=== Release 1.0.0 (In progress)
* https://github.com/spring-io/initializr/issues/83[#83]: add dependency scope support.
* https://github.com/spring-io/initializr/issues/81[#81]: allow baseDir parameter with sub-directories.
* https://github.com/spring-io/initializr/issues/80[#80]: upgrade to Gradle 2.3.
* https://github.com/spring-io/initializr/issues/62[#62]: add version range support.

View File

@@ -283,6 +283,12 @@ class InitializrMetadata {
@ToString(ignoreNulls = true, includePackage = false)
static class Dependency extends IdentifiableElement {
static final String SCOPE_COMPILE = 'compile'
static final String SCOPE_RUNTIME = 'runtime'
static final String SCOPE_PROVIDED = 'provided'
static final String SCOPE_TEST = 'test'
static final List<String> SCOPE_ALL = [SCOPE_COMPILE, SCOPE_RUNTIME, SCOPE_PROVIDED, SCOPE_TEST]
List<String> aliases = []
List<String> facets = []
@@ -293,10 +299,19 @@ class InitializrMetadata {
String version
String scope = SCOPE_COMPILE
String description
String versionRange
void setScope(String scope) {
if (!SCOPE_ALL.contains(scope)) {
throw new InvalidInitializrMetadataException("Invalid scope $scope must be one of $SCOPE_ALL")
}
this.scope = scope
}
void setVersionRange(String versionRange) {
this.versionRange = versionRange ? versionRange.trim() : null
}
@@ -313,12 +328,13 @@ class InitializrMetadata {
* Define this dependency as a standard spring boot starter with the specified name
* <p>If no name is specified, the root 'spring-boot-starter' is assumed.
*/
def asSpringBootStarter(String name) {
Dependency asSpringBootStarter(String name) {
groupId = 'org.springframework.boot'
artifactId = name ? 'spring-boot-starter-' + name : 'spring-boot-starter'
if (name) {
id = name
}
this
}
/**

View File

@@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.util.Assert
import static io.spring.initializr.InitializrMetadata.Dependency.*
import static io.spring.initializr.support.GroovyTemplate.template
/**
@@ -167,11 +168,17 @@ class ProjectGenerator {
request.resolve(metadata)
// request resolved so we can log what has been requested
def dependencies = request.resolvedDependencies.collect { it.id }
log.info("Processing request{type=$request.type, dependencies=$dependencies}")
def dependencies = request.resolvedDependencies
def dependencyIds = dependencies.collect { it.id }
log.info("Processing request{type=$request.type, dependencies=$dependencyIds}")
request.properties.each { model[it.key] = it.value }
model['compileDependencies'] = filterDependencies(dependencies, SCOPE_COMPILE)
model['runtimeDependencies'] = filterDependencies(dependencies, SCOPE_RUNTIME)
model['providedDependencies'] = filterDependencies(dependencies, SCOPE_PROVIDED)
model['testDependencies'] = filterDependencies(dependencies, SCOPE_TEST)
// @SpringBootApplication available as from 1.2.0.RC1
model['useSpringBootApplication'] = VERSION_1_2_0_RC1
.compareTo(Version.safeParse(request.bootVersion)) <= 0
@@ -212,4 +219,8 @@ class ProjectGenerator {
content << file
}
private static def filterDependencies(def dependencies, String scope) {
dependencies.findAll { dep -> scope.equals(dep.scope) }.sort { a, b -> a.id <=> b.id }
}
}

View File

@@ -39,11 +39,14 @@ repositories {
providedRuntime
}
<% } %>
dependencies {<% resolvedDependencies.each { %>
dependencies {<% compileDependencies.each { %>
compile("${it.groupId}:${it.artifactId}")<% } %><% if (language=='groovy') { %>
compile("org.codehaus.groovy:groovy")<% } %><% if (packaging=='war') { %>
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")<% } %>
testCompile("org.springframework.boot:spring-boot-starter-test")
compile("org.codehaus.groovy:groovy")<% } %><% runtimeDependencies.each { %>
runtime("${it.groupId}:${it.artifactId}")<% } %><% if (packaging=='war') { %>
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")<% } %><% providedDependencies.each { %>
providedRuntime("${it.groupId}:${it.artifactId}")<% } %>
testCompile("org.springframework.boot:spring-boot-starter-test") <% testDependencies.each { %>
testCompile("${it.groupId}:${it.artifactId}")<% } %>
}
eclipse {

View File

@@ -24,7 +24,7 @@
<java.version>${javaVersion}</java.version>
</properties>
<dependencies><% resolvedDependencies.each { %>
<dependencies><% compileDependencies.each { %>
<dependency>
<groupId>${it.groupId}</groupId>
<artifactId>${it.artifactId}</artifactId><% if (it.version != null) { %>
@@ -33,17 +33,36 @@
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
</dependency><% } %>
<% runtimeDependencies.each { %>
<dependency>
<groupId>${it.groupId}</groupId>
<artifactId>${it.artifactId}</artifactId><% if (it.version != null) { %>
<version>${it.version}</version><% } %>
<scope>runtime</scope>
</dependency><% } %><% if (packaging=='war') { %>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency><% } %><% providedDependencies.each { %>
<dependency>
<groupId>${it.groupId}</groupId>
<artifactId>${it.artifactId}</artifactId><% if (it.version != null) { %>
<version>${it.version}</version><% } %>
<scope>provided</scope>
</dependency><% } %>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependency><% testDependencies.each { %>
<dependency>
<groupId>${it.groupId}</groupId>
<artifactId>${it.artifactId}</artifactId><% if (it.version != null) { %>
<version>${it.version}</version><% } %>
<scope>test</scope>
</dependency><% } %>
</dependencies>
<build>

View File

@@ -97,6 +97,14 @@ class InitializrMetadataTests {
metadata.validateDependency(new InitializrMetadata.Dependency())
}
@Test
void invalidDependencyScope() {
def dependency = createDependency('web')
thrown.expect(InvalidInitializrMetadataException)
dependency.setScope('whatever')
}
@Test
void invalidSpringBootRange() {
def dependency = createDependency('web')

View File

@@ -130,9 +130,9 @@ class ProjectGeneratorTests {
def request = createProjectRequest('thymeleaf')
request.packaging = 'war'
generateMavenPom(request).hasStartClass('demo.DemoApplication')
.hasSpringBootStarterDependency('tomcat')
.hasSpringBootStarterTomcat()
.hasDependency('org.foo', 'thymeleaf') // This is tagged as web facet so it brings the web one
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
.hasDependenciesCount(3)
}
@@ -141,10 +141,10 @@ class ProjectGeneratorTests {
def request = createProjectRequest('data-jpa')
request.packaging = 'war'
generateMavenPom(request).hasStartClass('demo.DemoApplication')
.hasSpringBootStarterDependency('tomcat')
.hasSpringBootStarterTomcat()
.hasSpringBootStarterDependency('data-jpa')
.hasSpringBootStarterDependency('web') // Added by war packaging
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
.hasDependenciesCount(4)
}
@@ -223,7 +223,7 @@ class ProjectGeneratorTests {
@Test
void groovyWithMavenUsesJavaDir() {
def request = createProjectRequest('web')
request.type = 'maven-project'
request.type = 'maven-project'
request.language = 'groovy'
generateProject(request).isMavenProject().isGroovyProject()
}
@@ -231,11 +231,52 @@ class ProjectGeneratorTests {
@Test
void groovyWithGradleUsesGroovyDir() {
def request = createProjectRequest('web')
request.type = 'gradle-project'
request.type = 'gradle-project'
request.language = 'groovy'
generateProject(request).isGradleProject().isGroovyProject()
}
@Test
void mavenPomWithCustomScope() {
def h2 = new InitializrMetadata.Dependency(id: 'h2', groupId: 'org.h2', artifactId: 'h2', scope: 'runtime')
def hamcrest = new InitializrMetadata.Dependency(id: 'hamcrest', groupId: 'org.hamcrest',
artifactId: 'hamcrest', scope: 'test')
def servlet = new InitializrMetadata.Dependency(id: 'servlet-api', groupId: 'javax.servlet',
artifactId: 'servlet-api', scope: 'provided')
def metadata = InitializrMetadataBuilder.withDefaults()
.addDependencyGroup('core', 'web', 'security', 'data-jpa')
.addDependencyGroup('database', h2)
.addDependencyGroup('container', servlet)
.addDependencyGroup('test', hamcrest).validateAndGet()
projectGenerator.metadata = metadata
def request = createProjectRequest('hamcrest', 'h2', 'servlet-api', 'data-jpa', 'web')
generateMavenPom(request).hasDependency(h2).hasDependency(hamcrest).hasDependency(servlet)
.hasSpringBootStarterDependency('data-jpa')
.hasSpringBootStarterDependency('web')
}
@Test
void gradleBuildWithCustomScope() {
def h2 = new InitializrMetadata.Dependency(id: 'h2', groupId: 'org.h2', artifactId: 'h2', scope: 'runtime')
def hamcrest = new InitializrMetadata.Dependency(id: 'hamcrest', groupId: 'org.hamcrest',
artifactId: 'hamcrest', scope: 'test')
def servlet = new InitializrMetadata.Dependency(id: 'servlet-api', groupId: 'javax.servlet',
artifactId: 'servlet-api', scope: 'provided')
def metadata = InitializrMetadataBuilder.withDefaults()
.addDependencyGroup('core', 'web', 'security', 'data-jpa')
.addDependencyGroup('database', h2)
.addDependencyGroup('container', servlet)
.addDependencyGroup('test', hamcrest).validateAndGet()
projectGenerator.metadata = metadata
def request = createProjectRequest('hamcrest', 'h2', 'servlet-api', 'data-jpa', 'web')
generateGradleBuild(request)
.contains("compile(\"org.springframework.boot:spring-boot-starter-web\")")
.contains("compile(\"org.springframework.boot:spring-boot-starter-data-jpa\")")
.contains("runtime(\"org.h2:h2\")")
.contains("providedRuntime(\"javax.servlet:servlet-api\")")
.contains("testCompile(\"org.hamcrest:hamcrest\")")
}
PomAssert generateMavenPom(ProjectRequest request) {
def content = new String(projectGenerator.generateMavenPom(request))
new PomAssert(content).validateProjectRequest(request)

View File

@@ -60,7 +60,7 @@ class GradleBuildAssert {
}
GradleBuildAssert contains(String expression) {
assertTrue "$expression has not been found in gradle build", content.contains(expression)
assertTrue "$expression has not been found in gradle build $content", content.contains(expression)
this
}
}

View File

@@ -25,6 +25,7 @@ import org.w3c.dom.Document
import org.w3c.dom.Element
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertFalse
import static org.junit.Assert.assertNotNull
/**
@@ -109,6 +110,14 @@ class PomAssert {
this
}
PomAssert hasSpringBootStarterTomcat() {
hasDependency(new InitializrMetadata.Dependency(id: 'tomcat', scope: 'provided').asSpringBootStarter('tomcat'))
}
PomAssert hasSpringBootStarterTest() {
hasDependency(new InitializrMetadata.Dependency(id: 'test', scope: 'test').asSpringBootStarter('test'))
}
PomAssert hasSpringBootStarterDependency(String dependency) {
hasDependency('org.springframework.boot', "spring-boot-starter-$dependency")
}
@@ -122,11 +131,18 @@ class PomAssert {
}
PomAssert hasDependency(String groupId, String artifactId, String version) {
def id = generateId(groupId, artifactId)
hasDependency(new InitializrMetadata.Dependency(groupId: groupId, artifactId: artifactId, version: version))
}
PomAssert hasDependency(InitializrMetadata.Dependency expected) {
def id = generateId(expected.groupId, expected.artifactId)
def dependency = dependencies[id]
assertNotNull "No dependency found with '$id' --> ${dependencies.keySet()}", dependency
if (version) {
assertEquals "Wrong version for $dependency", version, dependency.version
if (expected.version) {
assertEquals "Wrong version for $dependency", expected.version, dependency.version
}
if (expected.scope) {
assertEquals "Wrong scope for $dependency", expected.scope, dependency.scope
}
this
}
@@ -189,7 +205,13 @@ class PomAssert {
if (version.length > 0) {
dependency.version = version.item(0).textContent
}
dependencies[dependency.generateId()] = dependency
def scope = element.getElementsByTagName('scope')
if (scope.length > 0) {
dependency.scope = scope.item(0).textContent
}
def id = dependency.generateId()
assertFalse("Duplicate dependency with id $id", dependencies.containsKey(id))
dependencies[id] = dependency
}
}
}

View File

@@ -38,7 +38,7 @@ class CommandLineExampleIntegrationTests extends AbstractInitializrControllerInt
downloadZip('/starter.zip').isJavaProject()
.isMavenProject().hasStaticAndTemplatesResources(false).pomAssert()
.hasSpringBootStarterRootDependency()
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
.hasDependenciesCount(2)
}
@@ -48,7 +48,7 @@ class CommandLineExampleIntegrationTests extends AbstractInitializrControllerInt
.isMavenProject().hasStaticAndTemplatesResources(true).pomAssert()
.hasJavaVersion('1.8')
.hasSpringBootStarterDependency('web')
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
.hasDependenciesCount(2)
}

View File

@@ -52,7 +52,7 @@ class MainControllerEnvIntegrationTests extends AbstractInitializrControllerInte
.hasStaticAndTemplatesResources(false).pomAssert()
.hasDependenciesCount(2)
.hasSpringBootStarterDependency('data-jpa')
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
}
}

View File

@@ -19,6 +19,7 @@ package io.spring.initializr.web
import java.nio.charset.Charset
import groovy.json.JsonSlurper
import io.spring.initializr.InitializrMetadata
import io.spring.initializr.InitializrMetadataVersion
import org.json.JSONObject
import org.junit.Ignore
@@ -56,7 +57,7 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
.hasDependenciesCount(3)
.hasSpringBootStarterDependency('web')
.hasSpringBootStarterDependency('data-jpa') // alias jpa -> data-jpa
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
}
@Test
@@ -69,10 +70,12 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
@Test
void dependencyInRange() {
def biz = new InitializrMetadata.Dependency(id: 'biz', groupId: 'org.acme',
artifactId: 'biz', version: '1.3.5', scope: 'runtime')
downloadTgz('/starter.tgz?style=org.acme:biz&bootVersion=1.2.1.RELEASE').isJavaProject().isMavenProject()
.hasStaticAndTemplatesResources(false).pomAssert()
.hasDependenciesCount(2)
.hasDependency('org.acme', 'biz', '1.3.5')
.hasDependency(biz)
}
@Test
@@ -90,7 +93,7 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
.hasStaticAndTemplatesResources(false).pomAssert()
.hasDependenciesCount(2)
.hasSpringBootStarterRootDependency() // the root dep is added if none is specified
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
}
@Test
@@ -100,7 +103,7 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
.hasDependenciesCount(3)
.hasSpringBootStarterDependency('web')
.hasSpringBootStarterDependency('data-jpa') // alias jpa -> data-jpa
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
}
@Test
@@ -110,7 +113,7 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
.hasDependenciesCount(3)
.hasSpringBootStarterDependency('web')
.hasSpringBootStarterDependency('data-jpa') // alias jpa -> data-jpa
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
}
@Test

View File

@@ -74,7 +74,7 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
projectAssert.hasBaseDir("demo").isMavenProject().isJavaProject()
.hasStaticAndTemplatesResources(false)
.pomAssert().hasDependenciesCount(2)
.hasSpringBootStarterRootDependency().hasSpringBootStarterDependency('test')
.hasSpringBootStarterRootDependency().hasSpringBootStarterTest()
}
}
@@ -88,7 +88,7 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
projectAssert.hasBaseDir('demo').isMavenProject().isGroovyProject()
.hasStaticAndTemplatesResources(false)
.pomAssert().hasDependenciesCount(3)
.hasSpringBootStarterRootDependency().hasSpringBootStarterDependency('test')
.hasSpringBootStarterRootDependency().hasSpringBootStarterTest()
.hasDependency('org.codehaus.groovy', 'groovy')
}
}
@@ -113,7 +113,7 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
.hasName('My project').hasDescription('A description for my project')
.hasSpringBootStarterDependency('web')
.hasSpringBootStarterDependency('data-jpa')
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
}
}
@@ -137,7 +137,7 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
.hasName('My Groovy project').hasDescription('A description for my Groovy project')
.hasSpringBootStarterDependency('web')
.hasSpringBootStarterDependency('data-jpa')
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTest()
.hasDependency('org.codehaus.groovy', 'groovy')
}
}
@@ -167,8 +167,8 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
.isJavaWarProject()
.pomAssert().hasPackaging('war').hasDependenciesCount(3)
.hasSpringBootStarterDependency('web') // Added with war packaging
.hasSpringBootStarterDependency('tomcat')
.hasSpringBootStarterDependency('test')
.hasSpringBootStarterTomcat()
.hasSpringBootStarterTest()
}
}

View File

@@ -30,12 +30,19 @@ initializr:
- name: Biz
groupId: org.acme
artifactId: biz
scope: runtime
version: 1.3.5
versionRange: 1.2.0.BUILD-SNAPSHOT
- name: Bur
id: org.acme:bur
version: 2.1.0
scope: test
versionRange: "[1.1.4.RELEASE,1.2.0.BUILD-SNAPSHOT)"
- name: My API
id : my-api
groupId: org.acme
artifactId: my-api
scope: provided
types:
- name: Maven POM
id: maven-build

View File

@@ -48,6 +48,10 @@
{
"id": "org.acme:bar",
"name": "Bar"
},
{
"id": "my-api",
"name": "My API"
}
]
}

View File

@@ -58,6 +58,10 @@
"id": "org.acme:bur",
"name": "Bur",
"versionRange": "[1.1.4.RELEASE,1.2.0.BUILD-SNAPSHOT)"
},
{
"id": "my-api",
"name": "My API"
}
]
}