Add support for custom dependency

Prior to this commit, only spring boot starters can be added as project
dependency using a simple String denoting the suffix of the artifactId.
The standard 'org.springframework.boot' and 'spring-boot-starter-'
artifactId prefix were assumed.

This commit allows to define arbitrary dependencies with arbitrary
identifiers; the groupId, artifactId and version of the dependency can
be specified. Internally, all dependencies are converted to that format
even the ones defined as standard spring boot starters.

To allow that, a ProjectRequest is now resolved against the initializr
metadata. If a request defines an unknown dependency, a simple String
will be still considered a spring-boot-starter but a more complex
unknown id will lead to an exception (e.g. 'org.foo:bar').

Fixes gh-17
This commit is contained in:
Stephane Nicoll
2014-08-19 04:07:13 +02:00
parent 964aef8bdb
commit 3849a7b5b9
15 changed files with 527 additions and 114 deletions

View File

@@ -9,84 +9,84 @@ info:
initializr: initializr:
dependencies: dependencies:
- name: Core - name: Core
starters: content:
- name: Security - name: Security
value: security id: security
- name: AOP - name: AOP
value: aop id: aop
- name: Data - name: Data
starters: content:
- name: JDBC - name: JDBC
value: jdbc id: jdbc
- name: JPA - name: JPA
value: data-jpa id: data-jpa
- name: MongoDB - name: MongoDB
value: data-mongodb id: data-mongodb
- name: Redis - name: Redis
value: redis id: redis
- name: Gemfire - name: Gemfire
value: data-gemfire id: data-gemfire
- name: Solr - name: Solr
value: data-solr id: data-solr
- name: I/O - name: I/O
starters: content:
- name: Batch - name: Batch
value: batch id: batch
- name: Integration - name: Integration
value: integration id: integration
- name: AMQP - name: AMQP
value: amqp id: amqp
- name: Web - name: Web
starters: content:
- name: Web - name: Web
value: web id: web
- name: Websocket - name: Websocket
value: websocket id: websocket
- name: Rest Repositories - name: Rest Repositories
value: data-rest id: data-rest
- name: Mobile - name: Mobile
value: mobile id: mobile
- name: Template Engines - name: Template Engines
starters: content:
- name: Freemarker - name: Freemarker
value: freemarker id: freemarker
- name: Velocity - name: Velocity
value: velocity id: velocity
- name: Groovy Templates - name: Groovy Templates
value: groovy-templates id: groovy-templates
- name: Thymeleaf - name: Thymeleaf
value: thymeleaf id: thymeleaf
- name: Social - name: Social
starters: content:
- name: Facebook - name: Facebook
value: social-facebook id: social-facebook
- name: LinkedIn - name: LinkedIn
value: social-linkedin id: social-linkedin
- name: Twitter - name: Twitter
value: social-twitter id: social-twitter
- name: Ops - name: Ops
starters: content:
- name: Actuator - name: Actuator
value: actuator id: actuator
- name: Remote Shell - name: Remote Shell
value: remote-shell id: remote-shell
types: types:
- name: Maven POM - name: Maven POM
id: pom.xml id: pom.xml
default: false default: false
action: /pom.xml action: /pom.xml
- name: Maven Project - name: Maven Project
id: starter.zip id: starter.zip
default: true default: true
action: /starter.zip action: /starter.zip
- name: Gradle Config - name: Gradle Config
id: build.gradle id: build.gradle
default: false default: false
action: /build.gradle action: /build.gradle
- name: Gradle Project - name: Gradle Project
id: gradle.zip id: gradle.zip
default: false default: false
action: /starter.zip action: /starter.zip
packagings: packagings:
- name: Jar - name: Jar
id: jar id: jar

View File

@@ -1,7 +1,26 @@
/*
* Copyright 2012-2014 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
*
* http://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 package io.spring.initializr
import javax.annotation.PostConstruct
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.annotation.JsonInclude
import groovy.transform.ToString
import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.ConfigurationProperties
@@ -36,6 +55,17 @@ class InitializrMetadata {
final Defaults defaults = new Defaults() final Defaults defaults = new Defaults()
@JsonIgnore
final Map<String, Dependency> indexedDependencies = new HashMap<String, Dependency>()
/**
* Return the {@link Dependency} with the specified id or {@code null} if
* no such dependency exists.
*/
Dependency getDependency(String id) {
return indexedDependencies.get(id)
}
/** /**
* Initializes a {@link ProjectRequest} instance with the defaults * Initializes a {@link ProjectRequest} instance with the defaults
* defined in this instance. * defined in this instance.
@@ -54,6 +84,54 @@ class InitializrMetadata {
request request
} }
/**
* Initialize and validate the configuration.
*/
@PostConstruct
void validate() {
for (DependencyGroup group : dependencies) {
for (Dependency dependency : group.getContent()) {
validateDependency(dependency)
indexDependency(dependency.id, dependency)
}
}
}
private void indexDependency(String id, Dependency dependency) {
Dependency existing = indexedDependencies.get(id)
if (existing != null) {
throw new IllegalArgumentException('Could not register ' + dependency +
': another dependency has also the "' + id + '" id ' + existing)
}
indexedDependencies.put(id, dependency)
}
static void validateDependency(Dependency dependency) {
String id = dependency.getId()
if (id == null) {
if (!dependency.hasCoordinates()) {
throw new InvalidInitializrMetadataException('Invalid dependency, ' +
'should have at least an id or a groupId/artifactId pair.')
}
dependency.generateId()
} else if (!dependency.hasCoordinates()) {
// Let's build the coordinates from the id
StringTokenizer st = new StringTokenizer(id, ':')
if (st.countTokens() == 1) { // assume spring-boot-starter
dependency.asSpringBootStarter(id)
} else if (st.countTokens() == 2 || st.countTokens() == 3) {
dependency.groupId = st.nextToken()
dependency.artifactId = st.nextToken()
if (st.hasMoreTokens()) {
dependency.version = st.nextToken()
}
} else {
throw new InvalidInitializrMetadataException('Invalid dependency, id should ' +
'have the form groupId:artifactId[:version] but got ' + id)
}
}
}
static def getDefault(List elements, String defaultValue) { static def getDefault(List elements, String defaultValue) {
for (DefaultIdentifiableElement element : elements) { for (DefaultIdentifiableElement element : elements) {
if (element.default) { if (element.default) {
@@ -68,10 +146,52 @@ class InitializrMetadata {
String name String name
final List<Map<String,Object>> starters= new ArrayList<>() final List<Dependency> content = new ArrayList<Dependency>()
} }
@ToString(ignoreNulls = true, includePackage = false)
static class Dependency extends IdentifiableElement {
@JsonIgnore
String groupId
@JsonIgnore
String artifactId
@JsonIgnore
String version
/**
* Specify if the dependency has its coordinates set, i.e. {@code groupId}
* and {@code artifactId}.
*/
boolean hasCoordinates() {
return groupId != null && artifactId != null
}
/**
* Define this dependency as a standard spring boot starter with the specified name
*/
def asSpringBootStarter(String name) {
groupId = 'org.springframework.boot'
artifactId = 'spring-boot-starter-' + name
}
/**
* Generate an id using the groupId and artifactId
*/
def generateId() {
if (groupId == null || artifactId == null) {
throw new IllegalArgumentException('Could not generate id for ' + this
+ ': at least groupId and artifactId must be set.')
}
StringBuilder sb = new StringBuilder()
sb.append(groupId).append(':').append(artifactId)
id = sb.toString()
}
}
static class Type extends DefaultIdentifiableElement { static class Type extends DefaultIdentifiableElement {
String action String action

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2012-2014 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
*
* http://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
/**
* Thrown when the configuration defines invalid metadata.
*
* @author Stephane Nicoll
* @since 1.0
*/
class InvalidInitializrMetadataException extends RuntimeException {
InvalidInitializrMetadataException(String s) {
super(s)
}
}

View File

@@ -123,27 +123,12 @@ class ProjectGenerator {
private Map initializeModel(ProjectRequest request) { private Map initializeModel(ProjectRequest request) {
Assert.notNull request.bootVersion, 'boot version must not be null' Assert.notNull request.bootVersion, 'boot version must not be null'
if (request.packaging == 'war' && !request.isWebStyle()) {
request.style << 'web'
}
def model = [:] def model = [:]
request.resolve(metadata)
request.properties.each { model[it.key] = it.value } request.properties.each { model[it.key] = it.value }
model.styles = fixStyles(request.style)
model model
} }
private def fixStyles(def style) {
if (style == null || style.size() == 0) {
style = ['']
}
if (!style.class.isArray() && !(style instanceof Collection)) {
style = [style]
}
style = style.collect { it == 'jpa' ? 'data-jpa' : it }
style.collect { it == '' ? '' : '-' + it }
}
private byte[] doGenerateMavenPom(Map model) { private byte[] doGenerateMavenPom(Map model) {
template 'starter-pom.xml', model template 'starter-pom.xml', model
} }

View File

@@ -16,6 +16,9 @@
package io.spring.initializr package io.spring.initializr
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/** /**
* A request to generate a project. * A request to generate a project.
* *
@@ -25,6 +28,8 @@ package io.spring.initializr
*/ */
class ProjectRequest { class ProjectRequest {
private static final Logger logger = LoggerFactory.getLogger(ProjectRequest.class)
def style = [] def style = []
String name String name
@@ -39,6 +44,37 @@ class ProjectRequest {
String packageName String packageName
String javaVersion String javaVersion
def dependencies = []
/**
* Resolve this instance against the specified {@link InitializrMetadata}
*/
void resolve(InitializrMetadata metadata) {
if (packaging == 'war' && !isWebStyle()) {
style << 'web'
}
if (style == null || style.size() == 0) {
style = []
}
if (!style.class.isArray() && !(style instanceof Collection)) {
style = [style]
}
style = style.collect { it == 'jpa' ? 'data-jpa' : it }
style.collect { it == '' ? '' : '-' + it }
dependencies = style.collect {
InitializrMetadata.Dependency dependency = metadata.getDependency(it)
if (dependency == null) {
if (it.contains(':')) {
throw new IllegalArgumentException('Unknown dependency ' + it + ' check project metadata')
}
logger.warn('No known dependency for style ' + it + ' assuming spring-boot-starter')
dependency = new InitializrMetadata.Dependency()
dependency.asSpringBootStarter(it)
}
dependency
}
}
boolean isWebStyle() { boolean isWebStyle() {
style.any { webStyle(it) } style.any { webStyle(it) }
} }

View File

@@ -110,10 +110,10 @@
<% dependencies.each { %> <% dependencies.each { %>
<div class="form-group col-sm-6"> <div class="form-group col-sm-6">
<h4>${it.name}</h4> <h4>${it.name}</h4>
<% it.starters.each { %> <% it.content.each { %>
<div class="checkbox"> <div class="checkbox">
<label> <label>
<input type="checkbox" name="style" value="${it.value}"> <input type="checkbox" name="style" value="${it.id}">
${it.name} ${it.name}
</label> </label>
</div><% } %> </div><% } %>

View File

@@ -39,8 +39,8 @@ repositories {
providedRuntime providedRuntime
} }
<% } %> <% } %>
dependencies {<% styles.each { %> dependencies {<% dependencies.each { %>
compile("org.springframework.boot:spring-boot-starter${it}")<% } %><% if (language=='groovy') { %> compile("${it.groupId}:${it.artifactId}")<% } %><% if (language=='groovy') { %>
compile("org.codehaus.groovy:groovy")<% } %><% if (packaging=='war') { %> compile("org.codehaus.groovy:groovy")<% } %><% if (packaging=='war') { %>
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")<% } %> providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")<% } %>
testCompile("org.springframework.boot:spring-boot-starter-test") testCompile("org.springframework.boot:spring-boot-starter-test")

View File

@@ -18,10 +18,11 @@
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<dependencies><% styles.each { %> <dependencies><% dependencies.each { %>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>${it.groupId}</groupId>
<artifactId>spring-boot-starter${it}</artifactId> <artifactId>${it.artifactId}</artifactId><% if (it.version != null) { %>
<version>${it.version}</version><% } %>
</dependency><% } %><% if (language=='groovy') { %> </dependency><% } %><% if (language=='groovy') { %>
<dependency> <dependency>
<groupId>org.codehaus.groovy</groupId> <groupId>org.codehaus.groovy</groupId>

View File

@@ -16,15 +16,146 @@
package io.spring.initializr package io.spring.initializr
import io.spring.initializr.support.InitializrMetadataBuilder
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.ExpectedException
import static org.junit.Assert.assertEquals import static org.junit.Assert.*
/** /**
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
class InitializrMetadataTests { class InitializrMetadataTests {
@Rule
public final ExpectedException thrown = ExpectedException.none()
private final InitializrMetadata metadata = new InitializrMetadata()
@Test
void setCoordinatesFromId() {
InitializrMetadata.Dependency dependency = createDependency('org.foo:bar:1.2.3')
metadata.validateDependency(dependency)
assertEquals 'org.foo', dependency.groupId
assertEquals 'bar', dependency.artifactId
assertEquals '1.2.3', dependency.version
assertEquals 'org.foo:bar:1.2.3', dependency.id
}
@Test
void setCoordinatesFromIdNoVersion() {
InitializrMetadata.Dependency dependency = createDependency('org.foo:bar')
metadata.validateDependency(dependency)
assertEquals 'org.foo', dependency.groupId
assertEquals 'bar', dependency.artifactId
assertNull dependency.version
assertEquals 'org.foo:bar', dependency.id
}
@Test
void setIdFromCoordinates() {
InitializrMetadata.Dependency dependency = new InitializrMetadata.Dependency()
dependency.groupId = 'org.foo'
dependency.artifactId = 'bar'
dependency.version = '1.0'
metadata.validateDependency(dependency)
assertEquals 'org.foo:bar', dependency.id
}
@Test
void setIdFromCoordinatesNoVersion() {
InitializrMetadata.Dependency dependency = new InitializrMetadata.Dependency()
dependency.groupId = 'org.foo'
dependency.artifactId = 'bar'
metadata.validateDependency(dependency)
assertEquals 'org.foo:bar', dependency.id
}
@Test
void setIdFromSimpleName() {
InitializrMetadata.Dependency dependency = createDependency('web')
metadata.validateDependency(dependency)
assertEquals 'org.springframework.boot', dependency.groupId
assertEquals 'spring-boot-starter-web', dependency.artifactId
assertNull dependency.version
assertEquals 'web', dependency.id
}
@Test
void invalidDependency() {
thrown.expect(InvalidInitializrMetadataException)
metadata.validateDependency(new InitializrMetadata.Dependency())
}
@Test
void invalidIdFormatTooManyColons() {
InitializrMetadata.Dependency dependency = createDependency('org.foo:bar:1.0:test:external')
thrown.expect(InvalidInitializrMetadataException)
metadata.validateDependency(dependency)
}
@Test
void generateIdWithNoGroupId() {
InitializrMetadata.Dependency dependency = new InitializrMetadata.Dependency()
dependency.artifactId = 'bar'
thrown.expect(IllegalArgumentException)
dependency.generateId()
}
@Test
void generateIdWithNoArtifactId() {
InitializrMetadata.Dependency dependency = new InitializrMetadata.Dependency()
dependency.groupId = 'foo'
thrown.expect(IllegalArgumentException)
dependency.generateId()
}
@Test
void indexedDependencies() {
InitializrMetadata metadata = new InitializrMetadata()
InitializrMetadata.DependencyGroup group = new InitializrMetadata.DependencyGroup()
InitializrMetadata.Dependency dependency = createDependency('first')
group.content.add(dependency)
InitializrMetadata.Dependency dependency2 = createDependency('second')
group.content.add(dependency2)
metadata.dependencies.add(group)
metadata.validate()
assertSame dependency, metadata.getDependency('first')
assertSame dependency2, metadata.getDependency('second')
assertNull metadata.getDependency('anotherId')
}
@Test
void addTwoDependenciesWithSameId() {
InitializrMetadata metadata = new InitializrMetadata()
InitializrMetadata.DependencyGroup group = new InitializrMetadata.DependencyGroup()
InitializrMetadata.Dependency dependency = createDependency('conflict')
group.content.add(dependency)
InitializrMetadata.Dependency dependency2 = createDependency('conflict')
group.content.add(dependency2)
metadata.dependencies.add(group)
thrown.expect(IllegalArgumentException)
thrown.expectMessage('conflict')
metadata.validate()
}
@Test
void createProjectRequest() {
InitializrMetadata metadata = InitializrMetadataBuilder.withDefaults().get()
ProjectRequest request = doCreateProjectRequest(metadata)
assertEquals metadata.defaults.groupId, request.groupId
}
@Test @Test
void getDefaultNoDefault() { void getDefaultNoDefault() {
List elements = [] List elements = []
@@ -39,10 +170,23 @@ class InitializrMetadataTests {
assertEquals 'two', InitializrMetadata.getDefault(elements, 'three') assertEquals 'two', InitializrMetadata.getDefault(elements, 'three')
} }
private static ProjectRequest doCreateProjectRequest(InitializrMetadata metadata) {
ProjectRequest request = new ProjectRequest()
metadata.initializeProjectRequest(request)
request
}
private static InitializrMetadata.Dependency createDependency(String id) {
InitializrMetadata.Dependency dependency = new InitializrMetadata.Dependency()
dependency.id = id
dependency
}
private static InitializrMetadata.JavaVersion createJavaVersion(String version, boolean selected) { private static InitializrMetadata.JavaVersion createJavaVersion(String version, boolean selected) {
InitializrMetadata.JavaVersion javaVersion = new InitializrMetadata.JavaVersion() InitializrMetadata.JavaVersion javaVersion = new InitializrMetadata.JavaVersion()
javaVersion.id = version javaVersion.id = version
javaVersion.default = selected javaVersion.default = selected
javaVersion javaVersion
} }
} }

View File

@@ -50,19 +50,6 @@ class ProjectGeneratorTests {
.hasSnapshotRepository().hasSpringBootStarterDependency('web') .hasSnapshotRepository().hasSpringBootStarterDependency('web')
} }
@Test
void mavenWarPomWithoutWebFacet() {
ProjectRequest request = createProjectRequest('data-jpa')
request.packaging = 'war'
generateMavenPom(request).hasStartClass('demo.Application')
.hasSpringBootStarterDependency('tomcat')
.hasSpringBootStarterDependency('data-jpa')
.hasSpringBootStarterDependency('web') // Added by web facet
.hasSpringBootStarterDependency('test')
.hasDependenciesCount(4)
}
PomAssert generateMavenPom(ProjectRequest request) { PomAssert generateMavenPom(ProjectRequest request) {
String content = new String(projectGenerator.generateMavenPom(request)) String content = new String(projectGenerator.generateMavenPom(request))
return new PomAssert(content).validateProjectRequest(request) return new PomAssert(content).validateProjectRequest(request)

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2012-2014 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
*
* http://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
import io.spring.initializr.support.InitializrMetadataBuilder
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import static org.junit.Assert.assertEquals
/**
* @author Stephane Nicoll
*/
class ProjectRequestTests {
@Rule
public final ExpectedException thrown = ExpectedException.none()
@Test
void resolve() {
ProjectRequest request = new ProjectRequest()
InitializrMetadata metadata = InitializrMetadataBuilder.withDefaults()
.addDependencyGroup('code', 'web', 'security', 'spring-data').get()
request.style << 'web' << 'spring-data'
request.resolve(metadata)
assertBootStarter(request.dependencies.get(0), 'web')
assertBootStarter(request.dependencies.get(1), 'spring-data')
}
@Test
void resolveFullMetadata() {
ProjectRequest request = new ProjectRequest()
InitializrMetadata metadata = InitializrMetadataBuilder.withDefaults()
.addDependencyGroup('code', createDependency('org.foo', 'acme', '1.2.0')).get()
request.style << 'org.foo:acme'
request.resolve(metadata)
assertDependency(request.dependencies.get(0), 'org.foo', 'acme', '1.2.0')
}
@Test
void resolveUnknownSimpleIdAsSpringBootStarter() {
ProjectRequest request = new ProjectRequest()
InitializrMetadata metadata = InitializrMetadataBuilder.withDefaults()
.addDependencyGroup('code', 'org.foo:bar').get()
request.style << 'org.foo:bar' << 'foo-bar'
request.resolve(metadata)
assertDependency(request.dependencies.get(0), 'org.foo', 'bar', null)
assertBootStarter(request.dependencies.get(1), 'foo-bar')
}
@Test
void resolveUnknownDependency() {
ProjectRequest request = new ProjectRequest()
InitializrMetadata metadata = InitializrMetadataBuilder.withDefaults()
.addDependencyGroup('code', 'org.foo:bar').get()
request.style << 'org.foo:acme' // does not exist and
thrown.expect(IllegalArgumentException)
thrown.expectMessage('org.foo:acme')
request.resolve(metadata)
}
private static void assertBootStarter(InitializrMetadata.Dependency actual, String name) {
InitializrMetadata.Dependency expected = new InitializrMetadata.Dependency()
expected.asSpringBootStarter(name)
assertDependency(actual, expected.groupId, expected.artifactId, expected.version)
}
private static InitializrMetadata.Dependency createDependency(String groupId, String artifactId, String version) {
InitializrMetadata.Dependency dependency = new InitializrMetadata.Dependency()
dependency.groupId = groupId
dependency.artifactId = artifactId
dependency.version = version
dependency
}
private static void assertDependency(InitializrMetadata.Dependency actual, String groupId,
String artifactId, String version) {
assertEquals groupId, actual.groupId
assertEquals artifactId, actual.artifactId
assertEquals version, actual.version
}
}

View File

@@ -34,6 +34,7 @@ class InitializrMetadataBuilder {
} }
InitializrMetadata get() { InitializrMetadata get() {
metadata.validate()
metadata metadata
} }
@@ -41,15 +42,22 @@ class InitializrMetadataBuilder {
InitializrMetadata.DependencyGroup group = new InitializrMetadata.DependencyGroup() InitializrMetadata.DependencyGroup group = new InitializrMetadata.DependencyGroup()
group.name = name group.name = name
for (String id : ids) { for (String id : ids) {
Map<String, Object> starter = new HashMap<>() InitializrMetadata.Dependency dependency = new InitializrMetadata.Dependency()
starter.put('name', id) dependency.id = id
starter.put('value', id) group.content.add(dependency)
group.starters.add(starter)
} }
metadata.dependencies.add(group) metadata.dependencies.add(group)
this this
} }
InitializrMetadataBuilder addDependencyGroup(String name, InitializrMetadata.Dependency... dependencies) {
InitializrMetadata.DependencyGroup group = new InitializrMetadata.DependencyGroup()
group.name = name
group.content.addAll(dependencies)
metadata.dependencies.add(group)
this
}
InitializrMetadataBuilder addDefaults() { InitializrMetadataBuilder addDefaults() {
addDefaultTypes().addDefaultPackagings().addDefaultJavaVersions() addDefaultTypes().addDefaultPackagings().addDefaultJavaVersions()
.addDefaultLanguages().addDefaultBootVersions() .addDefaultLanguages().addDefaultBootVersions()

View File

@@ -16,6 +16,7 @@
package io.spring.initializr.support package io.spring.initializr.support
import io.spring.initializr.InitializrMetadata
import io.spring.initializr.ProjectRequest import io.spring.initializr.ProjectRequest
import org.custommonkey.xmlunit.SimpleNamespaceContext import org.custommonkey.xmlunit.SimpleNamespaceContext
import org.custommonkey.xmlunit.XMLUnit import org.custommonkey.xmlunit.XMLUnit
@@ -36,7 +37,7 @@ class PomAssert {
final XpathEngine eng final XpathEngine eng
final Document doc final Document doc
final Map<String, Dependency> dependencies = new HashMap<String, Dependency>() final Map<String, InitializrMetadata.Dependency> dependencies = new HashMap<String, InitializrMetadata.Dependency>()
PomAssert(String content) { PomAssert(String content) {
eng = XMLUnit.newXpathEngine() eng = XMLUnit.newXpathEngine()
@@ -170,7 +171,7 @@ class PomAssert {
for (int i = 0; i < nodes.length; i++) { for (int i = 0; i < nodes.length; i++) {
def item = nodes.item(i) def item = nodes.item(i)
if (item instanceof Element) { if (item instanceof Element) {
Dependency dependency = new Dependency() InitializrMetadata.Dependency dependency = new InitializrMetadata.Dependency()
Element element = (Element) item Element element = (Element) item
def groupId = element.getElementsByTagName('groupId') def groupId = element.getElementsByTagName('groupId')
if (groupId.length > 0) { if (groupId.length > 0) {
@@ -186,24 +187,14 @@ class PomAssert {
} }
dependencies.put(dependency.generateId(), dependency) dependencies.put(dependency.generateId(), dependency)
} }
} }
} }
private static String generateId(String groupId, String artifactId) { private static String generateId(String groupId, String artifactId) {
groupId + ':' + artifactId InitializrMetadata.Dependency dependency = new InitializrMetadata.Dependency()
dependency.groupId = groupId
dependency.artifactId = artifactId
dependency.generateId()
} }
private static class Dependency {
String groupId
String artifactId
String version
String generateId() {
generateId(groupId, artifactId)
}
}
} }

View File

@@ -43,10 +43,10 @@ class MainControllerIntegrationTests extends AbstractMainControllerIntegrationTe
@Test @Test
void simpleTgzProject() { void simpleTgzProject() {
downloadTgz('/starter.tgz?style=data-jpa').isJavaProject().isMavenProject() downloadTgz('/starter.tgz?style=org.acme:bar').isJavaProject().isMavenProject()
.hasStaticAndTemplatesResources(false).pomAssert() .hasStaticAndTemplatesResources(false).pomAssert()
.hasDependenciesCount(2) .hasDependenciesCount(2)
.hasSpringBootStarterDependency('data-jpa') .hasDependency('org.acme', 'bar', '2.1.0')
} }
@Test @Test

View File

@@ -6,13 +6,22 @@ info:
initializr: initializr:
dependencies: dependencies:
- name: Core - name: Core
starters: content:
- name: Web - name: Web
value: web id: web
- name: Security - name: Security
value: security id: security
- name: Data JPA - name: Data JPA
value: data-jpa id: data-jpa
- name: Other
content:
- name: Foo
groupId: org.acme
artifactId: foo
version: 1.3.5
- name: Bar
id: org.acme:bar
version: 2.1.0
types: types:
- name: Maven POM - name: Maven POM
id: pom.xml id: pom.xml