Support for Bill of Materials

Add explicit support for Bill Of Materials. When a dependency defines a
bom ID, the related bom is added to the project. The metadata are
validated on startup to make sure a dependency does not refer to an
unknown bom entry.

Closes gh-99
This commit is contained in:
Stephane Nicoll
2015-03-26 14:47:08 +01:00
parent adf5cf068e
commit ddfc57443e
19 changed files with 305 additions and 57 deletions

View File

@@ -17,6 +17,7 @@
package io.spring.initializr.generator package io.spring.initializr.generator
import groovy.util.logging.Slf4j import groovy.util.logging.Slf4j
import io.spring.initializr.metadata.BillOfMaterials
import io.spring.initializr.metadata.Dependency import io.spring.initializr.metadata.Dependency
import io.spring.initializr.metadata.InitializrMetadata import io.spring.initializr.metadata.InitializrMetadata
import io.spring.initializr.metadata.Type import io.spring.initializr.metadata.Type
@@ -59,6 +60,8 @@ class ProjectRequest {
// Resolved dependencies based on the ids provided by either "style" or "dependencies" // Resolved dependencies based on the ids provided by either "style" or "dependencies"
List<Dependency> resolvedDependencies List<Dependency> resolvedDependencies
final List<BillOfMaterials> boms = []
def facets = [] def facets = []
def build def build
@@ -92,6 +95,7 @@ class ProjectRequest {
} }
String actualBootVersion = bootVersion ?: metadata.bootVersions.default.id String actualBootVersion = bootVersion ?: metadata.bootVersions.default.id
Version requestedVersion = Version.parse(actualBootVersion) Version requestedVersion = Version.parse(actualBootVersion)
Set<String> bomIds = []
resolvedDependencies.each { resolvedDependencies.each {
it.facets.each { it.facets.each {
if (!facets.contains(it)) { if (!facets.contains(it)) {
@@ -105,6 +109,13 @@ class ProjectRequest {
"with Spring Boot $bootVersion") "with Spring Boot $bootVersion")
} }
} }
if (it.bom) {
String bomId = it.bom
if (!bomIds.contains(bomId)) {
bomIds << bomId
boms << metadata.configuration.env.boms[bomId]
}
}
} }
if (this.type) { if (this.type) {

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2012-2015 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.metadata
import groovy.transform.ToString
/**
* Define a Bill Of Materials to be represented in the generated project
* if a dependency refers to it.
*
* @author Stephane Nicoll
* @since 1.0
*/
@ToString(ignoreNulls = true, includePackage = false)
class BillOfMaterials {
String groupId
String artifactId
String version
}

View File

@@ -16,6 +16,7 @@
package io.spring.initializr.metadata package io.spring.initializr.metadata
import com.fasterxml.jackson.annotation.JsonInclude
import groovy.transform.ToString import groovy.transform.ToString
import io.spring.initializr.util.InvalidVersionException import io.spring.initializr.util.InvalidVersionException
import io.spring.initializr.util.VersionRange import io.spring.initializr.util.VersionRange
@@ -28,6 +29,7 @@ import io.spring.initializr.util.VersionRange
* @since 1.0 * @since 1.0
*/ */
@ToString(ignoreNulls = true, includePackage = false) @ToString(ignoreNulls = true, includePackage = false)
@JsonInclude(JsonInclude.Include.NON_NULL)
class Dependency extends MetadataElement { class Dependency extends MetadataElement {
static final String SCOPE_COMPILE = 'compile' static final String SCOPE_COMPILE = 'compile'
@@ -57,6 +59,8 @@ class Dependency extends MetadataElement {
String versionRange String versionRange
String bom
void setScope(String scope) { void setScope(String scope) {
if (!SCOPE_ALL.contains(scope)) { if (!SCOPE_ALL.contains(scope)) {
throw new InvalidInitializrMetadataException("Invalid scope $scope must be one of $SCOPE_ALL") throw new InvalidInitializrMetadataException("Invalid scope $scope must be one of $SCOPE_ALL")

View File

@@ -14,8 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package io.spring.initializr package io.spring.initializr.metadata
/** /**
* Various configuration options used by the service. * Various configuration options used by the service.
* *
@@ -113,6 +112,11 @@ class InitializrConfiguration {
*/ */
boolean forceSsl = true boolean forceSsl = true
/**
* The {@link BillOfMaterials} that are referenced in this instance.
*/
final Map<String, BillOfMaterials> boms = [:]
void setArtifactRepository(String artifactRepository) { void setArtifactRepository(String artifactRepository) {
if (!artifactRepository.endsWith('/')) { if (!artifactRepository.endsWith('/')) {
artifactRepository = artifactRepository + '/' artifactRepository = artifactRepository + '/'
@@ -126,7 +130,13 @@ class InitializrConfiguration {
fallbackApplicationName = other.fallbackApplicationName fallbackApplicationName = other.fallbackApplicationName
invalidApplicationNames = other.invalidApplicationNames invalidApplicationNames = other.invalidApplicationNames
forceSsl = other.forceSsl forceSsl = other.forceSsl
other.boms.each { id, bom ->
if (!boms[id]) {
boms[id] = bom
}
}
} }
} }
} }

View File

@@ -15,9 +15,6 @@
*/ */
package io.spring.initializr.metadata package io.spring.initializr.metadata
import io.spring.initializr.InitializrConfiguration
/** /**
* Meta-data used to generate a project. * Meta-data used to generate a project.
* *
@@ -86,6 +83,14 @@ class InitializrMetadata {
*/ */
void validate() { void validate() {
dependencies.validate() dependencies.validate()
for (Dependency dependency : dependencies.all) {
def boms = configuration.env.boms
if (dependency.bom && !boms[dependency.bom]) {
throw new InvalidInitializrMetadataException("Dependency $dependency " +
"defines an invalid BOM id $dependency.bom, available boms $boms")
}
}
} }
/** /**
@@ -147,7 +152,7 @@ class InitializrMetadata {
@Override @Override
String getContent() { String getContent() {
String value = super.getContent() String value = super.getContent()
value == null ? nameCapability.content.replace('-', '.') : value value ?: (nameCapability.content != null ? nameCapability.content.replace('-', '.') : null)
} }
} }

View File

@@ -20,7 +20,6 @@ import java.nio.charset.Charset
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import groovy.util.logging.Log import groovy.util.logging.Log
import io.spring.initializr.InitializrConfiguration
import org.springframework.core.io.Resource import org.springframework.core.io.Resource
import org.springframework.util.StreamUtils import org.springframework.util.StreamUtils

View File

@@ -17,7 +17,6 @@
package io.spring.initializr.metadata package io.spring.initializr.metadata
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import io.spring.initializr.InitializrConfiguration
import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.context.properties.ConfigurationProperties

View File

@@ -17,6 +17,7 @@
package io.spring.initializr.metadata package io.spring.initializr.metadata
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonInclude
import org.springframework.util.Assert import org.springframework.util.Assert
@@ -28,6 +29,7 @@ import org.springframework.util.Assert
* @since 1.0 * @since 1.0
*/ */
@JsonIgnoreProperties(["default", "all"]) @JsonIgnoreProperties(["default", "all"])
@JsonInclude(JsonInclude.Include.NON_NULL)
abstract class ServiceCapability<T> { abstract class ServiceCapability<T> {
final String id final String id

View File

@@ -55,7 +55,7 @@ abstract class AbstractInitializrController {
/** /**
* Generate a full URL of the service, mostly for use in templates. * Generate a full URL of the service, mostly for use in templates.
* @see io.spring.initializr.InitializrConfiguration.Env#forceSsl * @see io.spring.initializr.metadata.InitializrConfiguration.Env#forceSsl
*/ */
protected String generateAppUrl() { protected String generateAppUrl() {
def builder = ServletUriComponentsBuilder.fromCurrentServletMapping() def builder = ServletUriComponentsBuilder.fromCurrentServletMapping()

View File

@@ -49,6 +49,13 @@ dependencies {<% compileDependencies.each { %>
testCompile("org.springframework.boot:spring-boot-starter-test") <% testDependencies.each { %> testCompile("org.springframework.boot:spring-boot-starter-test") <% testDependencies.each { %>
testCompile("${it.groupId}:${it.artifactId}${it.version ? ":$it.version" : ""}")<% } %> testCompile("${it.groupId}:${it.artifactId}${it.version ? ":$it.version" : ""}")<% } %>
} }
<% if (boms) { %>
dependencyManagement {
imports { <% boms.each { %>
mavenBom "${it.groupId}:${it.artifactId}${it.version ? ":$it.version" : ""}" <% } %>
}
}
<% } %>
eclipse { eclipse {
classpath { classpath {

View File

@@ -64,7 +64,19 @@
<scope>test</scope> <scope>test</scope>
</dependency><% } %> </dependency><% } %>
</dependencies> </dependencies>
<% if (boms) { %>
<dependencyManagement>
<dependencies><% boms.each { %>
<dependency>
<groupId>${it.groupId}</groupId>
<artifactId>${it.artifactId}</artifactId>
<version>${it.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency><% } %>
</dependencies>
</dependencyManagement>
<% } %>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>

View File

@@ -326,6 +326,46 @@ class ProjectGeneratorTests {
.doesNotContain("apply plugin: 'io.spring.dependency-management'") .doesNotContain("apply plugin: 'io.spring.dependency-management'")
} }
@Test
void mavenBom() {
def foo = new Dependency(id: 'foo', groupId: 'org.acme', artifactId: 'foo', bom: 'foo-bom')
def metadata = InitializrMetadataTestBuilder.withDefaults()
.addDependencyGroup('foo', foo)
.addBom('foo-bom', 'org.acme', 'foo-bom', '1.2.3').build()
projectGenerator.metadata = metadata
def request = createProjectRequest('foo')
generateMavenPom(request).hasDependency(foo)
.hasBom('org.acme', 'foo-bom', '1.2.3')
}
@Test
void mavenBomWithSeveralDependenciesOnSameBom() {
def foo = new Dependency(id: 'foo', groupId: 'org.acme', artifactId: 'foo', bom: 'the-bom')
def bar = new Dependency(id: 'bar', groupId: 'org.acme', artifactId: 'bar', bom: 'the-bom')
def metadata = InitializrMetadataTestBuilder.withDefaults()
.addDependencyGroup('group', foo, bar)
.addBom('the-bom', 'org.acme', 'the-bom', '1.2.3').build()
projectGenerator.metadata = metadata
def request = createProjectRequest('foo', 'bar')
generateMavenPom(request).hasDependency(foo)
.hasBom('org.acme', 'the-bom', '1.2.3')
.hasBomsCount(1)
}
@Test
void gradleBom() {
def foo = new Dependency(id: 'foo', groupId: 'org.acme', artifactId: 'foo', bom: 'foo-bom')
def metadata = InitializrMetadataTestBuilder.withDefaults()
.addDependencyGroup('foo', foo)
.addBom('foo-bom', 'org.acme', 'foo-bom', '1.2.3').build()
projectGenerator.metadata = metadata
def request = createProjectRequest('foo')
generateGradleBuild(request)
.contains("dependencyManagement {")
.contains("imports {")
.contains("mavenBom \"org.acme:foo-bom:1.2.3\"")
}
PomAssert generateMavenPom(ProjectRequest request) { PomAssert generateMavenPom(ProjectRequest request) {
def content = new String(projectGenerator.generateMavenPom(request)) def content = new String(projectGenerator.generateMavenPom(request))
new PomAssert(content).validateProjectRequest(request) new PomAssert(content).validateProjectRequest(request)

View File

@@ -14,8 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package io.spring.initializr package io.spring.initializr.metadata
import io.spring.initializr.metadata.InitializrConfiguration
import org.junit.Test import org.junit.Test
import static org.junit.Assert.assertEquals import static org.junit.Assert.assertEquals

View File

@@ -16,7 +16,6 @@
package io.spring.initializr.metadata package io.spring.initializr.metadata
import io.spring.initializr.InitializrConfiguration
import org.junit.Test import org.junit.Test
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean import org.springframework.beans.factory.config.YamlPropertiesFactoryBean
@@ -86,6 +85,26 @@ class InitializrMetadataBuilderTests {
assertEquals 'org.acme.demo', metadata.packageName.content assertEquals 'org.acme.demo', metadata.packageName.content
} }
@Test
void mergeMetadataWithBom() {
def metadata = InitializrMetadataBuilder.create().withInitializrMetadata(
new ClassPathResource('metadata/service/test-bom.json')).build()
def boms = metadata.configuration.env.boms
assertEquals 2, boms.size()
BillOfMaterials myBom = boms['my-bom']
assertNotNull myBom
assertEquals 'org.acme', myBom.groupId
assertEquals 'my-bom', myBom.artifactId
assertEquals '1.2.3.RELEASE', myBom.version
BillOfMaterials anotherBom = boms['another-bom']
assertNotNull anotherBom
assertEquals 'org.acme', anotherBom.groupId
assertEquals 'another-bom', anotherBom.artifactId
assertEquals '4.5.6.RELEASE', anotherBom.version
}
@Test @Test
void mergeConfigurationDisabledByDefault() { void mergeConfigurationDisabledByDefault() {
def config = load(new ClassPathResource("application-test-default.yml")) def config = load(new ClassPathResource("application-test-default.yml"))
@@ -116,13 +135,6 @@ class InitializrMetadataBuilderTests {
assertEquals false, actualEnv.forceSsl assertEquals false, actualEnv.forceSsl
} }
private static assertDefaultConfig(InitializrMetadata metadata) {
assertNotNull metadata
assertEquals "Wrong number of dependencies", 9, metadata.dependencies.all.size()
assertEquals "Wrong number of dependency group", 2, metadata.dependencies.content.size()
assertEquals "Wrong number of types", 4, metadata.types.content.size()
}
@Test @Test
void addDependencyInCustomizer() { void addDependencyInCustomizer() {
def group = new DependencyGroup(name: 'Extra') def group = new DependencyGroup(name: 'Extra')
@@ -138,6 +150,12 @@ class InitializrMetadataBuilderTests {
assertEquals group, metadata.dependencies.content[0] assertEquals group, metadata.dependencies.content[0]
} }
private static assertDefaultConfig(InitializrMetadata metadata) {
assertNotNull metadata
assertEquals "Wrong number of dependencies", 9, metadata.dependencies.all.size()
assertEquals "Wrong number of dependency group", 2, metadata.dependencies.content.size()
assertEquals "Wrong number of types", 4, metadata.types.content.size()
}
private static InitializrProperties load(Resource resource) { private static InitializrProperties load(Resource resource) {
PropertiesConfigurationFactory<InitializrProperties> factory = PropertiesConfigurationFactory<InitializrProperties> factory =

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2012-2015 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.metadata
import io.spring.initializr.test.InitializrMetadataTestBuilder
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
/**
* @author Stephane Nicoll
*/
class InitializrMetadataTests {
@Rule
public final ExpectedException thrown = ExpectedException.none()
@Test
void invalidBom() {
def foo = new Dependency(id: 'foo', groupId: 'org.acme', artifactId: 'foo', bom: 'foo-bom')
InitializrMetadataTestBuilder builder = InitializrMetadataTestBuilder
.withDefaults().addBom('my-bom', 'org.acme', 'foo', '1.2.3')
.addDependencyGroup('test', foo);
thrown.expect(InvalidInitializrMetadataException)
thrown.expectMessage("foo-bom")
thrown.expectMessage("my-bom")
builder.build()
}
}

View File

@@ -16,6 +16,7 @@
package io.spring.initializr.test package io.spring.initializr.test
import io.spring.initializr.metadata.BillOfMaterials
import io.spring.initializr.metadata.DefaultMetadataElement import io.spring.initializr.metadata.DefaultMetadataElement
import io.spring.initializr.metadata.Dependency import io.spring.initializr.metadata.Dependency
import io.spring.initializr.metadata.DependencyGroup import io.spring.initializr.metadata.DependencyGroup
@@ -23,7 +24,6 @@ import io.spring.initializr.metadata.InitializrMetadata
import io.spring.initializr.metadata.InitializrMetadataBuilder import io.spring.initializr.metadata.InitializrMetadataBuilder
import io.spring.initializr.metadata.Type import io.spring.initializr.metadata.Type
/** /**
* Easily create a {@link InitializrMetadata} instance for testing purposes. * Easily create a {@link InitializrMetadata} instance for testing purposes.
* *
@@ -140,4 +140,13 @@ class InitializrMetadataTestBuilder {
this this
} }
InitializrMetadataTestBuilder addBom(String id, String groupId, String artifactId, String version) {
builder.withCustomizer {
BillOfMaterials bom = new BillOfMaterials(
groupId: groupId, artifactId: artifactId, version: version)
it.configuration.env.boms[id] = bom
}
this
}
} }

View File

@@ -17,6 +17,7 @@
package io.spring.initializr.test package io.spring.initializr.test
import io.spring.initializr.generator.ProjectRequest import io.spring.initializr.generator.ProjectRequest
import io.spring.initializr.metadata.BillOfMaterials
import io.spring.initializr.metadata.Dependency import io.spring.initializr.metadata.Dependency
import org.custommonkey.xmlunit.SimpleNamespaceContext import org.custommonkey.xmlunit.SimpleNamespaceContext
import org.custommonkey.xmlunit.XMLUnit import org.custommonkey.xmlunit.XMLUnit
@@ -39,6 +40,7 @@ class PomAssert {
final XpathEngine eng final XpathEngine eng
final Document doc final Document doc
final Map<String, Dependency> dependencies = [:] final Map<String, Dependency> dependencies = [:]
final Map<String, BillOfMaterials> boms = [:]
PomAssert(String content) { PomAssert(String content) {
eng = XMLUnit.newXpathEngine() eng = XMLUnit.newXpathEngine()
@@ -48,6 +50,7 @@ class PomAssert {
eng.namespaceContext = namespaceContext eng.namespaceContext = namespaceContext
doc = XMLUnit.buildControlDocument(content) doc = XMLUnit.buildControlDocument(content)
parseDependencies() parseDependencies()
parseBoms()
} }
/** /**
@@ -135,7 +138,7 @@ class PomAssert {
} }
PomAssert hasDependency(Dependency expected) { PomAssert hasDependency(Dependency expected) {
def id = generateId(expected.groupId, expected.artifactId) def id = generateDependencyId(expected.groupId, expected.artifactId)
def dependency = dependencies[id] def dependency = dependencies[id]
assertNotNull "No dependency found with '$id' --> ${dependencies.keySet()}", dependency assertNotNull "No dependency found with '$id' --> ${dependencies.keySet()}", dependency
if (expected.version) { if (expected.version) {
@@ -147,6 +150,20 @@ class PomAssert {
this this
} }
PomAssert hasBom(String groupId, String artifactId, String version) {
def id = generateBomId(groupId, artifactId)
def bom = boms[id]
assertNotNull "No BOM found with '$id' --> ${boms.keySet()}", bom
assertEquals "Wrong version for $bom", version, bom.version
this
}
PomAssert hasBomsCount(int count) {
assertEquals "Wrong number of declared boms -->'${boms.keySet()}",
count, boms.size()
this
}
PomAssert hasNoRepository() { PomAssert hasNoRepository() {
assertEquals 0, eng.getMatchingNodes(createRootNodeXPath('repositories'), doc).length assertEquals 0, eng.getMatchingNodes(createRootNodeXPath('repositories'), doc).length
this this
@@ -216,7 +233,50 @@ class PomAssert {
} }
} }
private static String generateId(String groupId, String artifactId) { private def parseBoms() {
def nodes = eng.getMatchingNodes(createRootNodeXPath('dependencyManagement/pom:dependencies/pom:dependency'), doc)
for (int i = 0; i < nodes.length; i++) {
def item = nodes.item(i)
if (item instanceof Element) {
def element = (Element) item
def type = element.getElementsByTagName('type')
def scope = element.getElementsByTagName('scope')
if (isBom(type, scope)) {
def bom = new BillOfMaterials()
def groupId = element.getElementsByTagName('groupId')
if (groupId.length > 0) {
bom.groupId = groupId.item(0).textContent
}
def artifactId = element.getElementsByTagName('artifactId')
if (artifactId.length > 0) {
bom.artifactId = artifactId.item(0).textContent
}
def version = element.getElementsByTagName('version')
if (version.length > 0) {
bom.version = version.item(0).textContent
}
def id = generateBomId(bom.groupId, bom.artifactId)
assertFalse("Duplicate BOM with id $id", boms.containsKey(id))
boms[id] = bom
}
}
}
}
private static boolean isBom(def type, def scope) {
if (type.length == 0 || scope.length == 0) {
return false
}
String typeValue = type.item(0).textContent
String scopeValue = scope.item(0).textContent
return "pom".equals(typeValue) && "import".equals(scopeValue)
}
private static String generateBomId(def groupId, def artifactId) {
"$groupId:$artifactId"
}
private static String generateDependencyId(String groupId, String artifactId) {
def dependency = new Dependency() def dependency = new Dependency()
dependency.groupId = groupId dependency.groupId = groupId
dependency.artifactId = artifactId dependency.artifactId = artifactId

View File

@@ -0,0 +1,19 @@
{
"configuration": {
"env": {
"forceSsl": false,
"boms": {
"my-bom": {
"groupId": "org.acme",
"artifactId": "my-bom",
"version": "1.2.3.RELEASE"
},
"another-bom": {
"groupId": "org.acme",
"artifactId": "another-bom",
"version": "4.5.6.RELEASE"
}
}
}
}
}

View File

@@ -1,7 +1,6 @@
{ {
"artifactId": { "artifactId": {
"content": "demo", "content": "demo",
"description": null,
"id": "artifactId", "id": "artifactId",
"type": "TEXT" "type": "TEXT"
}, },
@@ -23,13 +22,13 @@
"name": "1.0.2" "name": "1.0.2"
} }
], ],
"description": null,
"id": "bootVersion", "id": "bootVersion",
"type": "SINGLE_SELECT" "type": "SINGLE_SELECT"
}, },
"configuration": { "configuration": {
"env": { "env": {
"artifactRepository": "https://repo.spring.io/release/", "artifactRepository": "https://repo.spring.io/release/",
"boms": {},
"fallbackApplicationName": "Application", "fallbackApplicationName": "Application",
"forceSsl": true, "forceSsl": true,
"invalidApplicationNames": [ "invalidApplicationNames": [
@@ -51,33 +50,25 @@
"groupId": "org.springframework.boot", "groupId": "org.springframework.boot",
"id": "web", "id": "web",
"name": "Web", "name": "Web",
"scope": "compile", "scope": "compile"
"version": null,
"versionRange": null
}, },
{ {
"aliases": [], "aliases": [],
"artifactId": "spring-boot-starter-security", "artifactId": "spring-boot-starter-security",
"description": null,
"facets": [], "facets": [],
"groupId": "org.springframework.boot", "groupId": "org.springframework.boot",
"id": "security", "id": "security",
"name": "Security", "name": "Security",
"scope": "compile", "scope": "compile"
"version": null,
"versionRange": null
}, },
{ {
"aliases": ["jpa"], "aliases": ["jpa"],
"artifactId": "spring-boot-starter-data-jpa", "artifactId": "spring-boot-starter-data-jpa",
"description": null,
"facets": [], "facets": [],
"groupId": "org.springframework.boot", "groupId": "org.springframework.boot",
"id": "data-jpa", "id": "data-jpa",
"name": "Data JPA", "name": "Data JPA",
"scope": "compile", "scope": "compile"
"version": null,
"versionRange": null
} }
], ],
"name": "Core" "name": "Core"
@@ -87,31 +78,26 @@
{ {
"aliases": [], "aliases": [],
"artifactId": "foo", "artifactId": "foo",
"description": null,
"facets": [], "facets": [],
"groupId": "org.acme", "groupId": "org.acme",
"id": "org.acme:foo", "id": "org.acme:foo",
"name": "Foo", "name": "Foo",
"scope": "compile", "scope": "compile",
"version": "1.3.5", "version": "1.3.5"
"versionRange": null
}, },
{ {
"aliases": [], "aliases": [],
"artifactId": "bar", "artifactId": "bar",
"description": null,
"facets": [], "facets": [],
"groupId": "org.acme", "groupId": "org.acme",
"id": "org.acme:bar", "id": "org.acme:bar",
"name": "Bar", "name": "Bar",
"scope": "compile", "scope": "compile",
"version": "2.1.0", "version": "2.1.0"
"versionRange": null
}, },
{ {
"aliases": [], "aliases": [],
"artifactId": "biz", "artifactId": "biz",
"description": null,
"facets": [], "facets": [],
"groupId": "org.acme", "groupId": "org.acme",
"id": "org.acme:biz", "id": "org.acme:biz",
@@ -123,7 +109,6 @@
{ {
"aliases": [], "aliases": [],
"artifactId": "bur", "artifactId": "bur",
"description": null,
"facets": [], "facets": [],
"groupId": "org.acme", "groupId": "org.acme",
"id": "org.acme:bur", "id": "org.acme:bur",
@@ -135,32 +120,26 @@
{ {
"aliases": [], "aliases": [],
"artifactId": "my-api", "artifactId": "my-api",
"description": null,
"facets": [], "facets": [],
"groupId": "org.acme", "groupId": "org.acme",
"id": "my-api", "id": "my-api",
"name": "My API", "name": "My API",
"scope": "provided", "scope": "provided"
"version": null,
"versionRange": null
} }
], ],
"name": "Other" "name": "Other"
} }
], ],
"description": null,
"id": "dependencies", "id": "dependencies",
"type": "HIERARCHICAL_MULTI_SELECT" "type": "HIERARCHICAL_MULTI_SELECT"
}, },
"description": { "description": {
"content": "Demo project for Spring Boot", "content": "Demo project for Spring Boot",
"description": null,
"id": "description", "id": "description",
"type": "TEXT" "type": "TEXT"
}, },
"groupId": { "groupId": {
"content": "org.test", "content": "org.test",
"description": null,
"id": "groupId", "id": "groupId",
"type": "TEXT" "type": "TEXT"
}, },
@@ -182,7 +161,6 @@
"name": "1.8" "name": "1.8"
} }
], ],
"description": null,
"id": "javaVersion", "id": "javaVersion",
"type": "SINGLE_SELECT" "type": "SINGLE_SELECT"
}, },
@@ -199,19 +177,16 @@
"name": "Java" "name": "Java"
} }
], ],
"description": null,
"id": "language", "id": "language",
"type": "SINGLE_SELECT" "type": "SINGLE_SELECT"
}, },
"name": { "name": {
"content": "demo", "content": "demo",
"description": null,
"id": "name", "id": "name",
"type": "TEXT" "type": "TEXT"
}, },
"packageName": { "packageName": {
"content": "demo", "content": "demo",
"description": null,
"id": "packageName", "id": "packageName",
"type": "TEXT" "type": "TEXT"
}, },
@@ -228,7 +203,6 @@
"name": "War" "name": "War"
} }
], ],
"description": null,
"id": "packaging", "id": "packaging",
"type": "SINGLE_SELECT" "type": "SINGLE_SELECT"
}, },
@@ -283,13 +257,11 @@
} }
} }
], ],
"description": null,
"id": "type", "id": "type",
"type": "ACTION" "type": "ACTION"
}, },
"version": { "version": {
"content": "0.0.1-SNAPSHOT", "content": "0.0.1-SNAPSHOT",
"description": null,
"id": "version", "id": "version",
"type": "TEXT" "type": "TEXT"
} }