mirror of
https://gitee.com/dcren/initializr.git
synced 2025-05-05 21:28:03 +08:00
Add version range support for dependencies
Previously, it was not possible to define the Spring Boot version that a dependency requires. As a result, many new starters implemented as part of Spring Boot 1.2 are not available. Each dependency can now define a `versionRange` attribute that defines the Spring Boot version range that it supports. The range is defined either as a single version, in which case it defines that version and any later versions or using brackets. A square bracket (`[` or `]`) denotes an inclusive range while a round bracket (`(` or `)`) denotes an exclusive range. Bumped the current-metadata format to 2.1 to include this additional `versionRange` attribute, that is application/vnd.initializr.v2.1+json Existing clients requesting v2 will not get any dependency that defines a `versionRange` attribute. Closes gh-62
This commit is contained in:
parent
8c0dc68a51
commit
da2ced86f3
@ -7,6 +7,7 @@ order.
|
|||||||
|
|
||||||
=== Release 1.0.0 (In progress)
|
=== Release 1.0.0 (In progress)
|
||||||
|
|
||||||
|
* https://github.com/spring-io/initializr/issues/62[#62]: add version range support.
|
||||||
* https://github.com/spring-io/initializr/issues/75[#75]: migrate smoke tests to Geb.
|
* https://github.com/spring-io/initializr/issues/75[#75]: migrate smoke tests to Geb.
|
||||||
* https://github.com/spring-io/initializr/issues/74[#74]: remove support for meta-data V1.
|
* https://github.com/spring-io/initializr/issues/74[#74]: remove support for meta-data V1.
|
||||||
* https://github.com/spring-io/initializr/issues/71[#71]: update project layout for Groovy-based projects.
|
* https://github.com/spring-io/initializr/issues/71[#71]: update project layout for Groovy-based projects.
|
||||||
|
@ -4,7 +4,7 @@ info:
|
|||||||
version: 0.3.1
|
version: 0.3.1
|
||||||
# remember to update static/install.sh as well:
|
# remember to update static/install.sh as well:
|
||||||
spring-boot:
|
spring-boot:
|
||||||
version: 1.2.0.RELEASE
|
version: 1.2.1.RELEASE
|
||||||
|
|
||||||
initializr:
|
initializr:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -16,6 +16,14 @@ initializr:
|
|||||||
- name: AOP
|
- name: AOP
|
||||||
id: aop
|
id: aop
|
||||||
description: Support for aspect-oriented programming including spring-aop and AspectJ
|
description: Support for aspect-oriented programming including spring-aop and AspectJ
|
||||||
|
- name: Atomikos (JTA)
|
||||||
|
id: jta-atomikos
|
||||||
|
description: Support for JTA distributed transactions via Atomikos
|
||||||
|
versionRange: 1.2.0.M1
|
||||||
|
- name: Bitronix (JTA)
|
||||||
|
id: jta-bitronix
|
||||||
|
description: Support for JTA distributed transactions via Bitronix
|
||||||
|
versionRange: 1.2.0.M1
|
||||||
- name: Data
|
- name: Data
|
||||||
content:
|
content:
|
||||||
- name: JDBC
|
- name: JDBC
|
||||||
@ -55,6 +63,10 @@ initializr:
|
|||||||
- name: AMQP
|
- name: AMQP
|
||||||
id: amqp
|
id: amqp
|
||||||
description: Support for the Advanced Message Queuing Protocol via spring-rabbit
|
description: Support for the Advanced Message Queuing Protocol via spring-rabbit
|
||||||
|
- name: Mail
|
||||||
|
id: mail
|
||||||
|
description: Support for javax.mail
|
||||||
|
versionRange: 1.2.0.RC1
|
||||||
- name: Web
|
- name: Web
|
||||||
content:
|
content:
|
||||||
- name: Web
|
- name: Web
|
||||||
@ -68,9 +80,17 @@ initializr:
|
|||||||
- name: WS
|
- name: WS
|
||||||
id: ws
|
id: ws
|
||||||
description: Support for Spring Web Services
|
description: Support for Spring Web Services
|
||||||
|
- name: Jersey (JAX-RS)
|
||||||
|
id: jersey
|
||||||
|
description: Support for the Jersey RESTful Web Services framework
|
||||||
|
versionRange: 1.2.0.RELEASE
|
||||||
- name: Rest Repositories
|
- name: Rest Repositories
|
||||||
id: data-rest
|
id: data-rest
|
||||||
description: Support for exposing Spring Data repositories over REST via spring-data-rest-webmvc
|
description: Support for exposing Spring Data repositories over REST via spring-data-rest-webmvc
|
||||||
|
- name: HATEOAS
|
||||||
|
id: hateoas
|
||||||
|
description: Support for HATEOAS-based RESTful services
|
||||||
|
versionRange: 1.2.2.BUILD-SNAPSHOT
|
||||||
- name: Mobile
|
- name: Mobile
|
||||||
id: mobile
|
id: mobile
|
||||||
description: Support for spring-mobile
|
description: Support for spring-mobile
|
||||||
@ -96,6 +116,12 @@ initializr:
|
|||||||
description: Support for the Thymeleaf templating engine, including integration with Spring
|
description: Support for the Thymeleaf templating engine, including integration with Spring
|
||||||
facets:
|
facets:
|
||||||
- web
|
- web
|
||||||
|
- name: Mustache
|
||||||
|
id: mustache
|
||||||
|
description: Support for the Mustache templating engine
|
||||||
|
versionRange: 1.2.2.BUILD-SNAPSHOT
|
||||||
|
facets:
|
||||||
|
- web
|
||||||
- name: Social
|
- name: Social
|
||||||
content:
|
content:
|
||||||
- name: Facebook
|
- name: Facebook
|
||||||
@ -112,6 +138,10 @@ initializr:
|
|||||||
- name: Actuator
|
- name: Actuator
|
||||||
id: actuator
|
id: actuator
|
||||||
description: Production ready features to help you monitor and manage your application
|
description: Production ready features to help you monitor and manage your application
|
||||||
|
- name: Cloud Connectors
|
||||||
|
id: cloud-connectors
|
||||||
|
description: Simplifies connecting to services in cloud platforms
|
||||||
|
versionRange: 1.2.0.RELEASE
|
||||||
- name: Remote Shell
|
- name: Remote Shell
|
||||||
id: remote-shell
|
id: remote-shell
|
||||||
description: Support for CRaSH
|
description: Support for CRaSH
|
||||||
|
@ -20,6 +20,11 @@ import javax.annotation.PostConstruct
|
|||||||
|
|
||||||
import groovy.transform.ToString
|
import groovy.transform.ToString
|
||||||
import groovy.util.logging.Slf4j
|
import groovy.util.logging.Slf4j
|
||||||
|
import io.spring.initializr.mapper.InitializrMetadataJsonMapper
|
||||||
|
import io.spring.initializr.mapper.InitializrMetadataV21JsonMapper
|
||||||
|
import io.spring.initializr.mapper.InitializrMetadataV2JsonMapper
|
||||||
|
import io.spring.initializr.support.InvalidVersionException
|
||||||
|
import io.spring.initializr.support.VersionRange
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
|
||||||
@ -61,8 +66,6 @@ class InitializrMetadata {
|
|||||||
|
|
||||||
private final Map<String, Dependency> indexedDependencies = [:]
|
private final Map<String, Dependency> indexedDependencies = [:]
|
||||||
|
|
||||||
private final transient InitializrMetadataJsonMapper jsonMapper = new InitializrMetadataJsonMapper()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the {@link Dependency} with the specified id or {@code null} if
|
* Return the {@link Dependency} with the specified id or {@code null} if
|
||||||
* no such dependency exists.
|
* no such dependency exists.
|
||||||
@ -127,11 +130,11 @@ class InitializrMetadata {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a JSON representation of the current metadata
|
* Generate a JSON representation of the current metadata
|
||||||
*
|
* @param version the meta-data version
|
||||||
* @param appUrl the application url
|
* @param appUrl the application url
|
||||||
*/
|
*/
|
||||||
String generateJson(String appUrl) {
|
String generateJson(InitializrMetadataVersion version, String appUrl) {
|
||||||
jsonMapper.write(this, appUrl)
|
getJsonMapper(version).write(this, appUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -193,6 +196,14 @@ class InitializrMetadata {
|
|||||||
"Invalid dependency, id should have the form groupId:artifactId[:version] but got $id")
|
"Invalid dependency, id should have the form groupId:artifactId[:version] but got $id")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (dependency.versionRange) {
|
||||||
|
try {
|
||||||
|
VersionRange.parse(dependency.versionRange)
|
||||||
|
} catch (InvalidVersionException ex) {
|
||||||
|
throw new InvalidInitializrMetadataException("Invalid version range '$dependency.versionRange' for " +
|
||||||
|
"dependency with id '$dependency.id'")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static def getDefault(List elements) {
|
static def getDefault(List elements) {
|
||||||
@ -205,6 +216,13 @@ class InitializrMetadata {
|
|||||||
return (elements.isEmpty() ? null : elements.get(0).id)
|
return (elements.isEmpty() ? null : elements.get(0).id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static InitializrMetadataJsonMapper getJsonMapper(InitializrMetadataVersion version) {
|
||||||
|
switch(version) {
|
||||||
|
case InitializrMetadataVersion.V2: return new InitializrMetadataV2JsonMapper();
|
||||||
|
default: return new InitializrMetadataV21JsonMapper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static class DependencyGroup {
|
static class DependencyGroup {
|
||||||
|
|
||||||
String name
|
String name
|
||||||
@ -228,6 +246,8 @@ class InitializrMetadata {
|
|||||||
|
|
||||||
String description
|
String description
|
||||||
|
|
||||||
|
String versionRange
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify if the dependency has its coordinates set, i.e. {@code groupId}
|
* Specify if the dependency has its coordinates set, i.e. {@code groupId}
|
||||||
* and {@code artifactId}.
|
* and {@code artifactId}.
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the supported meta-data version.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
enum InitializrMetadataVersion {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HAL-compliant metadata.
|
||||||
|
*/
|
||||||
|
V2('application/vnd.initializr.v2+json'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 'versionRange' attribute to any dependency to specify which
|
||||||
|
* Spring Boot versions are compatible with it.
|
||||||
|
*/
|
||||||
|
V2_1('application/vnd.initializr.v2.1+json')
|
||||||
|
|
||||||
|
private final MediaType mediaType;
|
||||||
|
|
||||||
|
InitializrMetadataVersion(String mediaType) {
|
||||||
|
this.mediaType = MediaType.parseMediaType(mediaType)
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaType getMediaType() {
|
||||||
|
return mediaType
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,6 +17,8 @@
|
|||||||
package io.spring.initializr
|
package io.spring.initializr
|
||||||
|
|
||||||
import groovy.util.logging.Slf4j
|
import groovy.util.logging.Slf4j
|
||||||
|
import io.spring.initializr.support.Version
|
||||||
|
import io.spring.initializr.support.VersionRange
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request to generate a project.
|
* A request to generate a project.
|
||||||
@ -79,12 +81,21 @@ class ProjectRequest {
|
|||||||
}
|
}
|
||||||
dependency
|
dependency
|
||||||
}
|
}
|
||||||
|
String actualBootVersion = bootVersion ?: metadata.defaults.bootVersion
|
||||||
|
Version requestedVersion = Version.parse(actualBootVersion)
|
||||||
resolvedDependencies.each {
|
resolvedDependencies.each {
|
||||||
it.facets.each {
|
it.facets.each {
|
||||||
if (!facets.contains(it)) {
|
if (!facets.contains(it)) {
|
||||||
facets.add(it)
|
facets.add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (it.versionRange) {
|
||||||
|
def range = VersionRange.parse(it.versionRange)
|
||||||
|
if (!range.match(requestedVersion)) {
|
||||||
|
throw new InvalidProjectRequestException("Dependency '$it.id' is not compatible " +
|
||||||
|
"with Spring Boot $bootVersion")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.type) {
|
if (this.type) {
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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.mapper
|
||||||
|
|
||||||
|
import io.spring.initializr.InitializrMetadata
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a JSON representation of the metadata.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface InitializrMetadataJsonMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a json representation of the specified meta-data.
|
||||||
|
*/
|
||||||
|
String write(InitializrMetadata metadata, String appUrl);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.mapper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link InitializrMetadataJsonMapper} handling the meta-data format for v2.1
|
||||||
|
* <p>
|
||||||
|
* Version 2.1 brings the 'versionRange' attribute for a dependency to restrict
|
||||||
|
* the Spring Boot versions that can be used against it.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class InitializrMetadataV21JsonMapper extends InitializrMetadataV2JsonMapper {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected mapDependency(dependency) {
|
||||||
|
def content = mapValue(dependency)
|
||||||
|
if (dependency.versionRange) {
|
||||||
|
content['versionRange'] = dependency.versionRange
|
||||||
|
}
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
@ -14,25 +14,27 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.spring.initializr
|
package io.spring.initializr.mapper
|
||||||
|
|
||||||
import groovy.json.JsonBuilder
|
import groovy.json.JsonBuilder
|
||||||
|
import io.spring.initializr.InitializrMetadata
|
||||||
|
|
||||||
import org.springframework.hateoas.TemplateVariable
|
import org.springframework.hateoas.TemplateVariable
|
||||||
import org.springframework.hateoas.TemplateVariables
|
import org.springframework.hateoas.TemplateVariables
|
||||||
import org.springframework.hateoas.UriTemplate
|
import org.springframework.hateoas.UriTemplate
|
||||||
|
import org.springframework.util.StringUtils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a JSON representation of the metadata.
|
* A {@link InitializrMetadataJsonMapper} handling the meta-data format for v2.
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
class InitializrMetadataJsonMapper {
|
class InitializrMetadataV2JsonMapper implements InitializrMetadataJsonMapper {
|
||||||
|
|
||||||
private final TemplateVariables templateVariables
|
private final TemplateVariables templateVariables
|
||||||
|
|
||||||
InitializrMetadataJsonMapper() {
|
InitializrMetadataV2JsonMapper() {
|
||||||
this.templateVariables = new TemplateVariables(
|
this.templateVariables = new TemplateVariables(
|
||||||
new TemplateVariable('dependencies', TemplateVariable.VariableType.REQUEST_PARAM),
|
new TemplateVariable('dependencies', TemplateVariable.VariableType.REQUEST_PARAM),
|
||||||
new TemplateVariable('packaging', TemplateVariable.VariableType.REQUEST_PARAM),
|
new TemplateVariable('packaging', TemplateVariable.VariableType.REQUEST_PARAM),
|
||||||
@ -48,6 +50,7 @@ class InitializrMetadataJsonMapper {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
String write(InitializrMetadata metadata, String appUrl) {
|
String write(InitializrMetadata metadata, String appUrl) {
|
||||||
JsonBuilder json = new JsonBuilder()
|
JsonBuilder json = new JsonBuilder()
|
||||||
json {
|
json {
|
||||||
@ -68,7 +71,7 @@ class InitializrMetadataJsonMapper {
|
|||||||
json.toString()
|
json.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
private links(parent, types, appUrl) {
|
protected links(parent, types, appUrl) {
|
||||||
def content = [:]
|
def content = [:]
|
||||||
types.each {
|
types.each {
|
||||||
content[it.id] = link(appUrl, it)
|
content[it.id] = link(appUrl, it)
|
||||||
@ -76,7 +79,7 @@ class InitializrMetadataJsonMapper {
|
|||||||
parent._links content
|
parent._links content
|
||||||
}
|
}
|
||||||
|
|
||||||
private link(appUrl, type) {
|
protected link(appUrl, type) {
|
||||||
def result = [:]
|
def result = [:]
|
||||||
result.href = generateTemplatedUri(appUrl, type)
|
result.href = generateTemplatedUri(appUrl, type)
|
||||||
result.templated = true
|
result.templated = true
|
||||||
@ -91,41 +94,40 @@ class InitializrMetadataJsonMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static dependencies(parent, groups) {
|
protected dependencies(parent, groups) {
|
||||||
parent.dependencies {
|
parent.dependencies {
|
||||||
type 'hierarchical-multi-select'
|
type 'hierarchical-multi-select'
|
||||||
values groups.collect {
|
values groups.collect {
|
||||||
processDependencyGroup(it)
|
mapDependencyGroup(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
protected type(parent, defaultValue, dependencies) {
|
||||||
|
|
||||||
private static type(parent, defaultValue, dependencies) {
|
|
||||||
parent.type {
|
parent.type {
|
||||||
type 'action'
|
type 'action'
|
||||||
if (defaultValue) {
|
if (defaultValue) {
|
||||||
'default' defaultValue
|
'default' defaultValue
|
||||||
}
|
}
|
||||||
values dependencies.collect {
|
values dependencies.collect {
|
||||||
processType(it)
|
mapType(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static singleSelect(parent, name, defaultValue, itemValues) {
|
protected singleSelect(parent, name, defaultValue, itemValues) {
|
||||||
parent."$name" {
|
parent."$name" {
|
||||||
type 'single-select'
|
type 'single-select'
|
||||||
if (defaultValue) {
|
if (defaultValue) {
|
||||||
'default' defaultValue
|
'default' defaultValue
|
||||||
}
|
}
|
||||||
values itemValues.collect {
|
values itemValues.collect {
|
||||||
processValue(it)
|
mapValue(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static text(parent, name, value) {
|
protected text(parent, name, value) {
|
||||||
parent."$name" {
|
parent."$name" {
|
||||||
type 'text'
|
type 'text'
|
||||||
if (value) {
|
if (value) {
|
||||||
@ -134,8 +136,7 @@ class InitializrMetadataJsonMapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected mapDependencyGroup(group) {
|
||||||
private static processDependencyGroup(group) {
|
|
||||||
def result = [:]
|
def result = [:]
|
||||||
result.name = group.name
|
result.name = group.name
|
||||||
if (group.hasProperty('description') && group.description) {
|
if (group.hasProperty('description') && group.description) {
|
||||||
@ -143,20 +144,29 @@ class InitializrMetadataJsonMapper {
|
|||||||
}
|
}
|
||||||
def items = []
|
def items = []
|
||||||
group.content.collect {
|
group.content.collect {
|
||||||
items << processValue(it)
|
def dependency = mapDependency(it)
|
||||||
|
if (dependency) {
|
||||||
|
items << dependency
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result.values = items
|
result.values = items
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
private static processType(type) {
|
protected mapDependency(dependency) {
|
||||||
def result = processValue(type)
|
if (!dependency.versionRange) { // only map the dependency if no versionRange is set
|
||||||
|
mapValue(dependency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected mapType(type) {
|
||||||
|
def result = mapValue(type)
|
||||||
result.action = type.action
|
result.action = type.action
|
||||||
result.tags = type.tags
|
result.tags = type.tags
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
private static processValue(value) {
|
protected mapValue(value) {
|
||||||
def result = [:]
|
def result = [:]
|
||||||
result.id = value.id
|
result.id = value.id
|
||||||
result.name = value.name
|
result.name = value.name
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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.support
|
||||||
|
|
||||||
|
import groovy.transform.InheritConstructors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if a input represents an invalid version.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@InheritConstructors
|
||||||
|
class InvalidVersionException extends RuntimeException {
|
||||||
|
|
||||||
|
}
|
@ -19,6 +19,8 @@ package io.spring.initializr.support
|
|||||||
import groovy.transform.EqualsAndHashCode
|
import groovy.transform.EqualsAndHashCode
|
||||||
import groovy.transform.ToString
|
import groovy.transform.ToString
|
||||||
|
|
||||||
|
import org.springframework.util.Assert
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the version number of a module. A typical version is represented
|
* Define the version number of a module. A typical version is represented
|
||||||
* as {@code MAJOR.MINOR.PATCH.QUALIFER} where the qualifier can have an
|
* as {@code MAJOR.MINOR.PATCH.QUALIFER} where the qualifier can have an
|
||||||
@ -49,16 +51,18 @@ class Version implements Comparable<Version> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the string representation of a {@link Version}. Throws an
|
* Parse the string representation of a {@link Version}. Throws an
|
||||||
* {@link IllegalArgumentException} if the version could not be parsed.
|
* {@link InvalidVersionException} if the version could not be parsed.
|
||||||
* @param text the version text
|
* @param text the version text
|
||||||
* @return a Version instance for the specified version text
|
* @return a Version instance for the specified version text
|
||||||
* @throws IllegalArgumentException if the version text could not be parsed
|
* @throws InvalidVersionException if the version text could not be parsed
|
||||||
* @see #safeParse(java.lang.String)
|
* @see #safeParse(java.lang.String)
|
||||||
*/
|
*/
|
||||||
static Version parse(String text) {
|
static Version parse(String text) {
|
||||||
def matcher = (text =~ VERSION_REGEX)
|
Assert.notNull(text, 'Text must not be null')
|
||||||
|
def matcher = (text.trim() =~ VERSION_REGEX)
|
||||||
if (!matcher.matches()) {
|
if (!matcher.matches()) {
|
||||||
throw new IllegalArgumentException("Could not determine version based on $text")
|
throw new InvalidVersionException("Could not determine version based on '$text': version format " +
|
||||||
|
"is Minor.Major.Patch.Qualifier (i.e. 1.0.5.RELEASE")
|
||||||
}
|
}
|
||||||
Version version = new Version()
|
Version version = new Version()
|
||||||
version.major = Integer.valueOf(matcher[0][1])
|
version.major = Integer.valueOf(matcher[0][1])
|
||||||
@ -87,7 +91,7 @@ class Version implements Comparable<Version> {
|
|||||||
static safeParse(String text) {
|
static safeParse(String text) {
|
||||||
try {
|
try {
|
||||||
return parse(text)
|
return parse(text)
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (InvalidVersionException e) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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.support
|
||||||
|
|
||||||
|
import groovy.transform.EqualsAndHashCode
|
||||||
|
import groovy.transform.ToString
|
||||||
|
|
||||||
|
import org.springframework.util.Assert
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a {@link Version} range. A square bracket "[" or "]" denotes an inclusive
|
||||||
|
* end of the range and a round bracket "(" or ")" denotes an exclusive end of the
|
||||||
|
* range. A range can also be unbounded by defining a a single {@link Version}. The
|
||||||
|
* examples below make this clear.
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>"[1.2.0.RELEASE,1.3.0.RELEASE)" version 1.2.0 and any version after
|
||||||
|
* this, up to, but not including, version 1.3.0.</li>
|
||||||
|
* <li>"(2.0.0.RELEASE,3.2.0.RELEASE]" any version after 2.0.0 up to and
|
||||||
|
* including version 3.2.0.</li>
|
||||||
|
* <li>"1.4.5.RELEASE", version 1.4.5 and all later versions.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
|
class VersionRange {
|
||||||
|
|
||||||
|
private static final String RANGE_REGEX = "(\\(|\\[)(.*),(.*)(\\)|\\])"
|
||||||
|
|
||||||
|
private Version lowerVersion
|
||||||
|
private boolean lowerInclusive
|
||||||
|
private Version higherVersion
|
||||||
|
private boolean higherInclusive
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify if the {@link Version} matches this range. Returns {@code true}
|
||||||
|
* if the version is contained within this range, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
boolean match(Version version) {
|
||||||
|
Assert.notNull(version, "Version must not be null")
|
||||||
|
def lower = lowerVersion.compareTo(version)
|
||||||
|
if (lower > 0) {
|
||||||
|
return false;
|
||||||
|
} else if (!lowerInclusive && lower == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (higherVersion) {
|
||||||
|
def higher = higherVersion.compareTo(version)
|
||||||
|
if (higher < 0) {
|
||||||
|
return false
|
||||||
|
} else if (!higherInclusive && higher == 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the string representation of a {@link VersionRange}. Throws an
|
||||||
|
* {@link InvalidVersionException} if the range could not be parsed.
|
||||||
|
* @param text the range text
|
||||||
|
* @return a VersionRange instance for the specified range text
|
||||||
|
* @throws InvalidVersionException if the range text could not be parsed
|
||||||
|
*/
|
||||||
|
static VersionRange parse(String text) {
|
||||||
|
Assert.notNull(text, "Text must not be null")
|
||||||
|
def matcher = (text.trim() =~ RANGE_REGEX)
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
// Try to read it as simple string
|
||||||
|
Version version = Version.parse(text)
|
||||||
|
return new VersionRange(lowerInclusive: true, lowerVersion: version)
|
||||||
|
}
|
||||||
|
VersionRange range = new VersionRange()
|
||||||
|
range.lowerInclusive = matcher[0][1].equals('[')
|
||||||
|
range.lowerVersion = Version.parse(matcher[0][2])
|
||||||
|
range.higherVersion = Version.parse(matcher[0][3])
|
||||||
|
range.higherInclusive = matcher[0][4].equals(']')
|
||||||
|
range
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,6 +18,7 @@ package io.spring.initializr.web
|
|||||||
|
|
||||||
import groovy.util.logging.Slf4j
|
import groovy.util.logging.Slf4j
|
||||||
import io.spring.initializr.CommandLineHelpGenerator
|
import io.spring.initializr.CommandLineHelpGenerator
|
||||||
|
import io.spring.initializr.InitializrMetadataVersion
|
||||||
import io.spring.initializr.ProjectGenerator
|
import io.spring.initializr.ProjectGenerator
|
||||||
import io.spring.initializr.ProjectRequest
|
import io.spring.initializr.ProjectRequest
|
||||||
|
|
||||||
@ -79,11 +80,20 @@ class MainController extends AbstractInitializrController {
|
|||||||
builder.body(commandLineHelpGenerator.generateGenericCapabilities(metadata, appUrl))
|
builder.body(commandLineHelpGenerator.generateGenericCapabilities(metadata, appUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/", produces = ["application/vnd.initializr.v2+json", "application/json"])
|
@RequestMapping(value = "/", produces = ["application/vnd.initializr.v2.1+json", "application/json"])
|
||||||
ResponseEntity<String> serviceCapabilities() {
|
ResponseEntity<String> serviceCapabilitiesV21() {
|
||||||
|
serviceCapabilitiesFor(InitializrMetadataVersion.V2_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/", produces = ["application/vnd.initializr.v2+json"])
|
||||||
|
ResponseEntity<String> serviceCapabilitiesV2() {
|
||||||
|
serviceCapabilitiesFor(InitializrMetadataVersion.V2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<String> serviceCapabilitiesFor(InitializrMetadataVersion version) {
|
||||||
String appUrl = ServletUriComponentsBuilder.fromCurrentServletMapping().build()
|
String appUrl = ServletUriComponentsBuilder.fromCurrentServletMapping().build()
|
||||||
def content = metadataProvider.get().generateJson(appUrl)
|
def content = metadataProvider.get().generateJson(version, appUrl)
|
||||||
return ResponseEntity.ok().contentType(META_DATA_V2).body(content)
|
return ResponseEntity.ok().contentType(version.mediaType).body(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = '/', produces = 'text/html')
|
@RequestMapping(value = '/', produces = 'text/html')
|
||||||
|
@ -97,6 +97,16 @@ class InitializrMetadataTests {
|
|||||||
metadata.validateDependency(new InitializrMetadata.Dependency())
|
metadata.validateDependency(new InitializrMetadata.Dependency())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void invalidSpringBootRange() {
|
||||||
|
def dependency = createDependency('web')
|
||||||
|
dependency.versionRange = 'A.B.C'
|
||||||
|
|
||||||
|
thrown.expect(InvalidInitializrMetadataException)
|
||||||
|
thrown.expectMessage('A.B.C')
|
||||||
|
metadata.validateDependency(dependency)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void invalidIdFormatTooManyColons() {
|
void invalidIdFormatTooManyColons() {
|
||||||
def dependency = createDependency('org.foo:bar:1.0:test:external')
|
def dependency = createDependency('org.foo:bar:1.0:test:external')
|
||||||
|
@ -95,6 +95,37 @@ class ProjectRequestTests {
|
|||||||
thrown.expect(InvalidProjectRequestException)
|
thrown.expect(InvalidProjectRequestException)
|
||||||
thrown.expectMessage('org.foo:acme')
|
thrown.expectMessage('org.foo:acme')
|
||||||
request.resolve(metadata)
|
request.resolve(metadata)
|
||||||
|
assertEquals(1, request.resolvedDependencies.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveDependencyInRange() {
|
||||||
|
def request = new ProjectRequest()
|
||||||
|
def dependency = createDependency('org.foo', 'bar', '1.2.0.RELEASE')
|
||||||
|
dependency.versionRange = '[1.0.1.RELEASE, 1.2.0.RELEASE)'
|
||||||
|
def metadata = InitializrMetadataBuilder.withDefaults()
|
||||||
|
.addDependencyGroup('code', dependency).validateAndGet()
|
||||||
|
|
||||||
|
request.style << 'org.foo:bar'
|
||||||
|
request.bootVersion = '1.1.2.RELEASE'
|
||||||
|
request.resolve(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resolveDependencyNotInRange() {
|
||||||
|
def request = new ProjectRequest()
|
||||||
|
def dependency = createDependency('org.foo', 'bar', '1.2.0.RELEASE')
|
||||||
|
dependency.versionRange = '[1.0.1.RELEASE, 1.2.0.RELEASE)'
|
||||||
|
def metadata = InitializrMetadataBuilder.withDefaults()
|
||||||
|
.addDependencyGroup('code', dependency).validateAndGet()
|
||||||
|
|
||||||
|
request.style << 'org.foo:bar'
|
||||||
|
request.bootVersion = '0.9.9.RELEASE'
|
||||||
|
|
||||||
|
thrown.expect(InvalidProjectRequestException)
|
||||||
|
thrown.expectMessage('org.foo:bar')
|
||||||
|
thrown.expectMessage('0.9.9.RELEASE')
|
||||||
|
request.resolve(metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -14,9 +14,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.spring.initializr
|
package io.spring.initializr.mapper
|
||||||
|
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
|
import io.spring.initializr.InitializrMetadata
|
||||||
import io.spring.initializr.test.InitializrMetadataBuilder
|
import io.spring.initializr.test.InitializrMetadataBuilder
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ import static org.junit.Assert.assertEquals
|
|||||||
*/
|
*/
|
||||||
class InitializrMetadataJsonMapperTests {
|
class InitializrMetadataJsonMapperTests {
|
||||||
|
|
||||||
private final InitializrMetadataJsonMapper jsonMapper = new InitializrMetadataJsonMapper()
|
private final InitializrMetadataJsonMapper jsonMapper = new InitializrMetadataV21JsonMapper()
|
||||||
private final JsonSlurper slurper = new JsonSlurper()
|
private final JsonSlurper slurper = new JsonSlurper()
|
||||||
|
|
||||||
@Test
|
@Test
|
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* 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.support
|
||||||
|
|
||||||
|
import org.hamcrest.BaseMatcher
|
||||||
|
import org.hamcrest.Description
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.ExpectedException
|
||||||
|
|
||||||
|
import static org.hamcrest.core.IsNot.not
|
||||||
|
import static org.junit.Assert.assertThat
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class VersionRangeTests {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final ExpectedException thrown = ExpectedException.none()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchSimpleRange() {
|
||||||
|
assertThat('1.2.0.RC3', match('[1.2.0.RC1,1.2.0.RC5]'))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchSimpleRangeBefore() {
|
||||||
|
assertThat('1.1.9.RC3', not(match('[1.2.0.RC1,1.2.0.RC5]')))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchSimpleRangeAfter() {
|
||||||
|
assertThat('1.2.0.RC6', not(match('[1.2.0.RC1,1.2.0.RC5]')))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchInclusiveLowerRange() {
|
||||||
|
assertThat('1.2.0.RC1', match('[1.2.0.RC1,1.2.0.RC5]'))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchInclusiveHigherRange() {
|
||||||
|
assertThat('1.2.0.RC5', match('[1.2.0.RC1,1.2.0.RC5]'))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchExclusiveLowerRange() {
|
||||||
|
assertThat('1.2.0.RC1', not(match('(1.2.0.RC1,1.2.0.RC5)')))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchExclusiveHigherRange() {
|
||||||
|
assertThat('1.2.0.RC5', not(match('[1.2.0.RC1,1.2.0.RC5)')))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchUnboundedRangeEqual() {
|
||||||
|
assertThat('1.2.0.RELEASE', match('1.2.0.RELEASE'))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchUnboundedRangeAfter() {
|
||||||
|
assertThat('2.2.0.RELEASE', match('1.2.0.RELEASE'))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchUnboundedRangeBefore() {
|
||||||
|
assertThat('1.1.9.RELEASE', not(match('1.2.0.RELEASE')))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void invalidRange() {
|
||||||
|
thrown.expect(InvalidVersionException)
|
||||||
|
VersionRange.parse("foo-bar")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rangeWithSpaces() {
|
||||||
|
assertThat('1.2.0.RC3', match('[ 1.2.0.RC1 , 1.2.0.RC5]'))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static VersionRangeMatcher match(String range) {
|
||||||
|
new VersionRangeMatcher(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class VersionRangeMatcher extends BaseMatcher<String> {
|
||||||
|
|
||||||
|
private final VersionRange range;
|
||||||
|
|
||||||
|
VersionRangeMatcher(String text) {
|
||||||
|
this.range = VersionRange.parse(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean matches(Object item) {
|
||||||
|
if (!item instanceof String) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.range.match(Version.parse(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void describeTo(Description description) {
|
||||||
|
description.appendText(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -130,7 +130,7 @@ class VersionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void parseInvalidVersion() {
|
void parseInvalidVersion() {
|
||||||
thrown.expect(IllegalArgumentException)
|
thrown.expect(InvalidVersionException)
|
||||||
parse('foo')
|
parse('foo')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,4 +139,9 @@ class VersionTests {
|
|||||||
assertNull safeParse('foo')
|
assertNull safeParse('foo')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parseVersionWithSpaces() {
|
||||||
|
assertThat(parse(' 1.2.0.RC3 '), lessThan(parse('1.3.0.RELEASE')))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,9 @@ package io.spring.initializr.web
|
|||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
|
import io.spring.initializr.InitializrMetadataVersion
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.skyscreamer.jsonassert.JSONAssert
|
import org.skyscreamer.jsonassert.JSONAssert
|
||||||
import org.skyscreamer.jsonassert.JSONCompareMode
|
import org.skyscreamer.jsonassert.JSONCompareMode
|
||||||
@ -43,8 +45,7 @@ import static org.junit.Assert.*
|
|||||||
@ActiveProfiles('test-default')
|
@ActiveProfiles('test-default')
|
||||||
class MainControllerIntegrationTests extends AbstractInitializrControllerIntegrationTests {
|
class MainControllerIntegrationTests extends AbstractInitializrControllerIntegrationTests {
|
||||||
|
|
||||||
private static final MediaType CURRENT_METADATA_MEDIA_TYPE =
|
private static final MediaType CURRENT_METADATA_MEDIA_TYPE = InitializrMetadataVersion.V2_1.mediaType
|
||||||
MediaType.parseMediaType('application/vnd.initializr.v2+json')
|
|
||||||
|
|
||||||
private final def slurper = new JsonSlurper()
|
private final def slurper = new JsonSlurper()
|
||||||
|
|
||||||
@ -60,10 +61,27 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void simpleTgzProject() {
|
void simpleTgzProject() {
|
||||||
downloadTgz('/starter.tgz?style=org.acme:bar').isJavaProject().isMavenProject()
|
downloadTgz('/starter.tgz?style=org.acme:foo').isJavaProject().isMavenProject()
|
||||||
.hasStaticAndTemplatesResources(false).pomAssert()
|
.hasStaticAndTemplatesResources(false).pomAssert()
|
||||||
.hasDependenciesCount(2)
|
.hasDependenciesCount(2)
|
||||||
.hasDependency('org.acme', 'bar', '2.1.0')
|
.hasDependency('org.acme', 'foo', '1.3.5')
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void dependencyInRange() {
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void dependencyNotInRange() {
|
||||||
|
try {
|
||||||
|
execute('/starter.tgz?style=org.acme:bur', byte[], null, null)
|
||||||
|
} catch (HttpClientErrorException ex) {
|
||||||
|
assertEquals HttpStatus.NOT_ACCEPTABLE, ex.statusCode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -136,12 +154,34 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void metadataWithCurrentAcceptHeader() {
|
@Ignore("Need a comparator that does not care about the number of elements in an array")
|
||||||
|
void currentMetadataCompatibleWithV2() {
|
||||||
|
ResponseEntity<String> response = invokeHome(null, '*/*')
|
||||||
|
validateMetadata(response, CURRENT_METADATA_MEDIA_TYPE, '2.0.0', JSONCompareMode.LENIENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void metadataWithV2AcceptHeader() {
|
||||||
ResponseEntity<String> response = invokeHome(null, 'application/vnd.initializr.v2+json')
|
ResponseEntity<String> response = invokeHome(null, 'application/vnd.initializr.v2+json')
|
||||||
|
validateMetadata(response, InitializrMetadataVersion.V2.mediaType, '2.0.0', JSONCompareMode.STRICT)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void metadataWithCurrentAcceptHeader() {
|
||||||
|
ResponseEntity<String> response = invokeHome(null, 'application/vnd.initializr.v2.1+json')
|
||||||
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE)
|
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE)
|
||||||
validateCurrentMetadata(new JSONObject(response.body))
|
validateCurrentMetadata(new JSONObject(response.body))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void metadataWithUnknownAcceptHeader() {
|
||||||
|
try {
|
||||||
|
invokeHome(null, 'application/vnd.initializr.v5.4+json')
|
||||||
|
} catch (HttpClientErrorException ex) {
|
||||||
|
assertEquals HttpStatus.NOT_ACCEPTABLE, ex.statusCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void curlReceivesTextByDefault() {
|
void curlReceivesTextByDefault() {
|
||||||
ResponseEntity<String> response = invokeHome('curl/1.2.4', "*/*")
|
ResponseEntity<String> response = invokeHome('curl/1.2.4', "*/*")
|
||||||
@ -210,13 +250,21 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
|
|||||||
validateCurrentMetadata(json)
|
validateCurrentMetadata(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateMetadata(ResponseEntity<String> response, MediaType mediaType,
|
||||||
|
String version, JSONCompareMode compareMode) {
|
||||||
|
validateContentType(response, mediaType)
|
||||||
|
def json = new JSONObject(response.body)
|
||||||
|
def expected = readJson(version)
|
||||||
|
JSONAssert.assertEquals(expected, json, compareMode)
|
||||||
|
}
|
||||||
|
|
||||||
private void validateCurrentMetadata(ResponseEntity<String> response) {
|
private void validateCurrentMetadata(ResponseEntity<String> response) {
|
||||||
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE)
|
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE)
|
||||||
validateCurrentMetadata(new JSONObject(response.body))
|
validateCurrentMetadata(new JSONObject(response.body))
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateCurrentMetadata(JSONObject json) {
|
private void validateCurrentMetadata(JSONObject json) {
|
||||||
def expected = readJson('2.0.0')
|
def expected = readJson('2.1.0')
|
||||||
JSONAssert.assertEquals(expected, json, JSONCompareMode.STRICT)
|
JSONAssert.assertEquals(expected, json, JSONCompareMode.STRICT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,8 +101,8 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
|
|||||||
page.artifactId = 'foo-bar'
|
page.artifactId = 'foo-bar'
|
||||||
page.name = 'My project'
|
page.name = 'My project'
|
||||||
page.description = 'A description for my project'
|
page.description = 'A description for my project'
|
||||||
page.dependency('web')
|
page.dependency('web').click()
|
||||||
page.dependency('data-jpa')
|
page.dependency('data-jpa').click()
|
||||||
page.generateProject.click()
|
page.generateProject.click()
|
||||||
at HomePage
|
at HomePage
|
||||||
def projectAssert = zipProjectAssert(from('foo-bar.zip'))
|
def projectAssert = zipProjectAssert(from('foo-bar.zip'))
|
||||||
@ -126,8 +126,8 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
|
|||||||
page.artifactId = 'groovy-project'
|
page.artifactId = 'groovy-project'
|
||||||
page.name = 'My Groovy project'
|
page.name = 'My Groovy project'
|
||||||
page.description = 'A description for my Groovy project'
|
page.description = 'A description for my Groovy project'
|
||||||
page.dependency('web')
|
page.dependency('web').click()
|
||||||
page.dependency('data-jpa')
|
page.dependency('data-jpa').click()
|
||||||
page.generateProject.click()
|
page.generateProject.click()
|
||||||
at HomePage
|
at HomePage
|
||||||
def projectAssert = zipProjectAssert(from('groovy-project.zip'))
|
def projectAssert = zipProjectAssert(from('groovy-project.zip'))
|
||||||
@ -147,7 +147,7 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
|
|||||||
void createSimpleGradleProject() {
|
void createSimpleGradleProject() {
|
||||||
toHome {
|
toHome {
|
||||||
page.type = 'gradle-project'
|
page.type = 'gradle-project'
|
||||||
page.dependency('data-jpa')
|
page.dependency('data-jpa').click()
|
||||||
page.generateProject.click()
|
page.generateProject.click()
|
||||||
at HomePage
|
at HomePage
|
||||||
def projectAssert = zipProjectAssert(from('demo.zip'))
|
def projectAssert = zipProjectAssert(from('demo.zip'))
|
||||||
@ -177,7 +177,7 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
|
|||||||
void createMavenBuild() {
|
void createMavenBuild() {
|
||||||
toHome {
|
toHome {
|
||||||
page.type = 'maven-build'
|
page.type = 'maven-build'
|
||||||
page.dependency('data-jpa')
|
page.dependency('data-jpa').click()
|
||||||
page.artifactId = 'my-maven-project'
|
page.artifactId = 'my-maven-project'
|
||||||
page.generateProject.click()
|
page.generateProject.click()
|
||||||
at HomePage
|
at HomePage
|
||||||
@ -200,6 +200,35 @@ class ProjectGenerationSmokeTests extends AbstractInitializrControllerIntegratio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void dependencyHiddenAccordingToRange() {
|
||||||
|
toHome { // bur: [1.1.4.RELEASE,1.2.0.BUILD-SNAPSHOT)
|
||||||
|
page.dependency('org.acme:bur').displayed == true
|
||||||
|
|
||||||
|
page.bootVersion = '1.0.2.RELEASE'
|
||||||
|
page.dependency('org.acme:bur').displayed == false
|
||||||
|
page.dependency('org.acme:biz').displayed == false
|
||||||
|
page.bootVersion = '1.1.4.RELEASE'
|
||||||
|
page.dependency('org.acme:bur').displayed == true
|
||||||
|
page.dependency('org.acme:biz').displayed == false
|
||||||
|
page.bootVersion = '1.2.0.BUILD-SNAPSHOT'
|
||||||
|
page.dependency('org.acme:bur').displayed == false
|
||||||
|
page.dependency('org.acme:biz').displayed == true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void dependencyUncheckedWhenHidden() {
|
||||||
|
toHome {
|
||||||
|
page.dependency('org.acme:bur').value() == 'org.acme:bur'
|
||||||
|
page.bootVersion = '1.0.2.RELEASE'
|
||||||
|
page.dependency('org.acme:bur').displayed == false
|
||||||
|
page.bootVersion = '1.1.4.RELEASE'
|
||||||
|
page.dependency('org.acme:bur').displayed == true
|
||||||
|
page.dependency('org.acme:bur').value() == false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Browser toHome(Closure script) {
|
private Browser toHome(Closure script) {
|
||||||
browser.go("http://localhost:" + port + "/")
|
browser.go("http://localhost:" + port + "/")
|
||||||
browser.at HomePage
|
browser.at HomePage
|
||||||
|
@ -39,7 +39,7 @@ class HomePage extends Page {
|
|||||||
language { $('form').language() }
|
language { $('form').language() }
|
||||||
|
|
||||||
dependency { id ->
|
dependency { id ->
|
||||||
$("form").find('input', type: "checkbox", name: "style", value: id).click()
|
$("form").find('input', type: "checkbox", name: "style", value: id)
|
||||||
}
|
}
|
||||||
generateProject { $('form').find('button', name: 'generate-project') }
|
generateProject { $('form').find('button', name: 'generate-project') }
|
||||||
|
|
||||||
|
@ -27,6 +27,15 @@ initializr:
|
|||||||
- name: Bar
|
- name: Bar
|
||||||
id: org.acme:bar
|
id: org.acme:bar
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
|
- name: Biz
|
||||||
|
groupId: org.acme
|
||||||
|
artifactId: biz
|
||||||
|
version: 1.3.5
|
||||||
|
versionRange: 1.2.0.BUILD-SNAPSHOT
|
||||||
|
- name: Bur
|
||||||
|
id: org.acme:bur
|
||||||
|
version: 2.1.0
|
||||||
|
versionRange: "[1.1.4.RELEASE,1.2.0.BUILD-SNAPSHOT)"
|
||||||
types:
|
types:
|
||||||
- name: Maven POM
|
- name: Maven POM
|
||||||
id: maven-build
|
id: maven-build
|
||||||
|
196
initializr/src/test/resources/metadata/test-default-2.1.0.json
Normal file
196
initializr/src/test/resources/metadata/test-default-2.1.0.json
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
{
|
||||||
|
"_links": {
|
||||||
|
"maven-build": {
|
||||||
|
"href": "http://localhost:@port@/pom.xml?type=maven-build{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
|
||||||
|
"templated": true
|
||||||
|
},
|
||||||
|
"maven-project": {
|
||||||
|
"href": "http://localhost:@port@/starter.zip?type=maven-project{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
|
||||||
|
"templated": true
|
||||||
|
},
|
||||||
|
"gradle-build": {
|
||||||
|
"href": "http://localhost:@port@/build.gradle?type=gradle-build{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
|
||||||
|
"templated": true
|
||||||
|
},
|
||||||
|
"gradle-project": {
|
||||||
|
"href": "http://localhost:@port@/starter.zip?type=gradle-project{&dependencies,packaging,javaVersion,language,bootVersion,groupId,artifactId,version,name,description,packageName}",
|
||||||
|
"templated": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"type": "hierarchical-multi-select",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"name": "Core",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"id": "web",
|
||||||
|
"name": "Web",
|
||||||
|
"description": "Web dependency description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "security",
|
||||||
|
"name": "Security"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "data-jpa",
|
||||||
|
"name": "Data JPA"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Other",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"id": "org.acme:foo",
|
||||||
|
"name": "Foo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "org.acme:bar",
|
||||||
|
"name": "Bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "org.acme:biz",
|
||||||
|
"name": "Biz",
|
||||||
|
"versionRange": "1.2.0.BUILD-SNAPSHOT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "org.acme:bur",
|
||||||
|
"name": "Bur",
|
||||||
|
"versionRange": "[1.1.4.RELEASE,1.2.0.BUILD-SNAPSHOT)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "action",
|
||||||
|
"default": "maven-project",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"id": "maven-build",
|
||||||
|
"name": "Maven POM",
|
||||||
|
"action": "/pom.xml",
|
||||||
|
"tags": {
|
||||||
|
"build": "maven",
|
||||||
|
"format": "build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "maven-project",
|
||||||
|
"name": "Maven Project",
|
||||||
|
"action": "/starter.zip",
|
||||||
|
"tags": {
|
||||||
|
"build": "maven",
|
||||||
|
"format": "project"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "gradle-build",
|
||||||
|
"name": "Gradle Config",
|
||||||
|
"action": "/build.gradle",
|
||||||
|
"tags": {
|
||||||
|
"build": "gradle",
|
||||||
|
"format": "build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "gradle-project",
|
||||||
|
"name": "Gradle Project",
|
||||||
|
"action": "/starter.zip",
|
||||||
|
"tags": {
|
||||||
|
"build": "gradle",
|
||||||
|
"format": "project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packaging": {
|
||||||
|
"type": "single-select",
|
||||||
|
"default": "jar",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"id": "jar",
|
||||||
|
"name": "Jar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "war",
|
||||||
|
"name": "War"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"javaVersion": {
|
||||||
|
"type": "single-select",
|
||||||
|
"default": "1.7",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"id": "1.6",
|
||||||
|
"name": "1.6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1.7",
|
||||||
|
"name": "1.7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1.8",
|
||||||
|
"name": "1.8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"type": "single-select",
|
||||||
|
"default": "java",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"id": "groovy",
|
||||||
|
"name": "Groovy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "java",
|
||||||
|
"name": "Java"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bootVersion": {
|
||||||
|
"type": "single-select",
|
||||||
|
"default": "1.1.4.RELEASE",
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"id": "1.2.0.BUILD-SNAPSHOT",
|
||||||
|
"name": "Latest SNAPSHOT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1.1.4.RELEASE",
|
||||||
|
"name": "1.1.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1.0.2.RELEASE",
|
||||||
|
"name": "1.0.2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"groupId": {
|
||||||
|
"type": "text",
|
||||||
|
"default": "org.test"
|
||||||
|
},
|
||||||
|
"artifactId": {
|
||||||
|
"type": "text",
|
||||||
|
"default": "demo"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "text",
|
||||||
|
"default": "0.0.1-SNAPSHOT"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "text",
|
||||||
|
"default": "demo"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "text",
|
||||||
|
"default": "Demo project for Spring Boot"
|
||||||
|
},
|
||||||
|
"packageName": {
|
||||||
|
"type": "text",
|
||||||
|
"default": "demo"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user