mirror of
https://gitee.com/dcren/initializr.git
synced 2025-12-26 22:25:51 +08:00
Explicit support for curl
Add an explicit support for curl by returning a text description of the service instead of the raw meta-data. curl users can still discover the json metadata by setting the appropriate Accept header. Also support for explicit "text/plain" if the user requires it. In this case a generic text description is returned. Closes gh-67
This commit is contained in:
@@ -7,6 +7,9 @@ order.
|
||||
|
||||
=== Release 1.0.0 (In progress)
|
||||
|
||||
* https://github.com/spring-io/initializr/issues/67[#67]: explicit support for curl by returning a text
|
||||
description of the service instead of the raw meta-data. curl users can still discover the json metadata
|
||||
by setting the appropriate Accept header.
|
||||
* https://github.com/spring-io/initializr/issues/65[#65]: project archives generated by the web UI now contain
|
||||
a base directory equals to the `artifactId`. By default, no base directory gets created if the project is
|
||||
generated by another means; use the new `baseDir` to control that behaviour.
|
||||
|
||||
@@ -118,6 +118,7 @@ initializr:
|
||||
types:
|
||||
- name: Maven POM
|
||||
id: maven-build
|
||||
description: Generate a Maven pom.xml
|
||||
sts-id: pom.xml
|
||||
tags:
|
||||
build: maven
|
||||
@@ -126,6 +127,7 @@ initializr:
|
||||
action: /pom.xml
|
||||
- name: Maven Project
|
||||
id: maven-project
|
||||
description: Generate a Maven based project archive
|
||||
sts-id: starter.zip
|
||||
tags:
|
||||
build: maven
|
||||
@@ -134,6 +136,7 @@ initializr:
|
||||
action: /starter.zip
|
||||
- name: Gradle Config
|
||||
id: gradle-build
|
||||
description: Generate a Gradle based project archive
|
||||
sts-id: build.gradle
|
||||
tags:
|
||||
build: gradle
|
||||
@@ -142,6 +145,7 @@ initializr:
|
||||
action: /build.gradle
|
||||
- name: Gradle Project
|
||||
id: gradle-project
|
||||
description: Generate a Gradle build file
|
||||
sts-id: gradle.zip
|
||||
tags:
|
||||
build: gradle
|
||||
|
||||
@@ -53,6 +53,12 @@
|
||||
<artifactId>groovy-json</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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 static io.spring.initializr.support.GroovyTemplate.template
|
||||
|
||||
/**
|
||||
* Generate help pages for command-line clients.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.0
|
||||
*/
|
||||
class CommandLineHelpGenerator {
|
||||
|
||||
private static final String logo = '''
|
||||
. ____ _ __ _ _
|
||||
/\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\
|
||||
( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\
|
||||
\\\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|
||||
' |____| .__|_| |_|_| |_\\__, | / / / /
|
||||
=========|_|==============|___/=/_/_/_/
|
||||
'''
|
||||
|
||||
/**
|
||||
* Generate the capabilities of the service as a generic plain text
|
||||
* document. Used when no particular agent was detected.
|
||||
*/
|
||||
String generateGenericCapabilities(InitializrMetadata metadata, String serviceUrl) {
|
||||
def model = initializeModel(metadata, serviceUrl)
|
||||
model['hasExamples'] = false
|
||||
doGenerateCapabilities(model)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the capabilities of the service using "curl" as a plain text
|
||||
* document.
|
||||
*/
|
||||
String generateCurlCapabilities(InitializrMetadata metadata, String serviceUrl) {
|
||||
def model = initializeModel(metadata, serviceUrl)
|
||||
model['examples'] = template 'curl-examples.txt', model
|
||||
model['hasExamples'] = true
|
||||
doGenerateCapabilities(model)
|
||||
}
|
||||
|
||||
private doGenerateCapabilities(def model) {
|
||||
template 'cli-capabilities.txt', model
|
||||
}
|
||||
|
||||
|
||||
private Map initializeModel(InitializrMetadata metadata, serviceUrl) {
|
||||
Map model = [:]
|
||||
model['logo'] = logo
|
||||
model['serviceUrl'] = serviceUrl
|
||||
|
||||
Map dependencies = [:]
|
||||
new ArrayList(metadata.allDependencies).sort { a, b -> a.id <=> b.id }.each {
|
||||
String description = it.name
|
||||
if (it.description) {
|
||||
description += ": $it.description"
|
||||
}
|
||||
dependencies[it.id] = description
|
||||
}
|
||||
model['dependencies'] = dependencies
|
||||
|
||||
Map types = [:]
|
||||
|
||||
new ArrayList<>(metadata.types).sort { a, b -> a.id <=> b.id }.each {
|
||||
String description = it.description
|
||||
if (!description) {
|
||||
description = it.name
|
||||
}
|
||||
types[it.id] = description
|
||||
}
|
||||
model['types'] = types
|
||||
|
||||
Map defaults = [:]
|
||||
metadata.defaults.properties.sort().each {
|
||||
if (!(it.key in ['class', 'metaClass', 'DEFAULT_NAME'])) {
|
||||
defaults[it.key] = it.value
|
||||
}
|
||||
}
|
||||
defaults['applicationName'] = ProjectRequest.generateApplicationName(metadata.defaults.name,
|
||||
ProjectRequest.DEFAULT_APPLICATION_NAME)
|
||||
model['defaults'] = defaults
|
||||
|
||||
model
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,26 @@
|
||||
/*
|
||||
* 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 java.util.concurrent.ConcurrentMap
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import io.spring.initializr.web.MainController
|
||||
import io.spring.initializr.web.WebConfig
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.actuate.metrics.CounterService
|
||||
@@ -37,6 +53,11 @@ class InitializrAutoConfiguration {
|
||||
@Autowired
|
||||
private CounterService counterService
|
||||
|
||||
@Bean
|
||||
WebConfig webConfig() {
|
||||
new WebConfig()
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MainController)
|
||||
MainController initializrMainController() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -75,6 +75,13 @@ class InitializrMetadata {
|
||||
indexedDependencies[id]
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all dependencies as a flat collection
|
||||
*/
|
||||
Collection<Dependency> getAllDependencies() {
|
||||
indexedDependencies.values()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Type} with the specified id or {@code null} if no
|
||||
* such type exists.
|
||||
@@ -267,6 +274,8 @@ class InitializrMetadata {
|
||||
|
||||
static class Type extends DefaultIdentifiableElement {
|
||||
|
||||
String description
|
||||
|
||||
@JsonIgnore
|
||||
@Deprecated
|
||||
String stsId
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.spring.initializr.web
|
||||
|
||||
import groovy.util.logging.Slf4j
|
||||
import io.spring.initializr.CommandLineHelpGenerator
|
||||
import io.spring.initializr.InitializrMetadata
|
||||
import io.spring.initializr.ProjectGenerator
|
||||
import io.spring.initializr.ProjectRequest
|
||||
@@ -24,11 +25,14 @@ import io.spring.initializr.ProjectRequest
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.web.bind.annotation.ModelAttribute
|
||||
import org.springframework.web.bind.annotation.RequestHeader
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.ResponseBody
|
||||
import org.springframework.web.context.request.WebRequest
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder
|
||||
|
||||
/**
|
||||
@@ -44,9 +48,14 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder
|
||||
@Slf4j
|
||||
class MainController extends AbstractInitializrController {
|
||||
|
||||
public static final MediaType META_DATA_V2 = MediaType.parseMediaType("application/vnd.initializr.v2+json")
|
||||
|
||||
@Autowired
|
||||
private ProjectGenerator projectGenerator
|
||||
|
||||
private CommandLineHelpGenerator commandLineHelpGenerator = new CommandLineHelpGenerator()
|
||||
|
||||
|
||||
@ModelAttribute
|
||||
ProjectRequest projectRequest() {
|
||||
def request = new ProjectRequest()
|
||||
@@ -61,11 +70,26 @@ class MainController extends AbstractInitializrController {
|
||||
metadataProvider.get()
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/", produces = ["application/vnd.initializr.v2+json","application/json"])
|
||||
@ResponseBody
|
||||
String metadata() {
|
||||
@RequestMapping(value = "/", produces = ["text/plain"])
|
||||
ResponseEntity<String> serviceCapabilities(
|
||||
@RequestHeader(value = HttpHeaders.USER_AGENT, required = false) String userAgent) {
|
||||
String appUrl = ServletUriComponentsBuilder.fromCurrentServletMapping().build()
|
||||
metadataProvider.get().generateJson(appUrl)
|
||||
def metadata = metadataProvider.get()
|
||||
|
||||
def builder = ResponseEntity.ok().contentType(MediaType.TEXT_PLAIN)
|
||||
if (userAgent && userAgent.startsWith(WebConfig.CURL_USER_AGENT_PREFIX)) {
|
||||
builder.body(commandLineHelpGenerator.generateCurlCapabilities(metadata,appUrl))
|
||||
}
|
||||
else {
|
||||
builder.body(commandLineHelpGenerator.generateGenericCapabilities(metadata, appUrl))
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/", produces = ["application/vnd.initializr.v2+json", "application/json"])
|
||||
ResponseEntity<String> serviceCapabilities() {
|
||||
String appUrl = ServletUriComponentsBuilder.fromCurrentServletMapping().build()
|
||||
def content = metadataProvider.get().generateJson(appUrl)
|
||||
return ResponseEntity.ok().contentType(META_DATA_V2).body(content)
|
||||
}
|
||||
|
||||
@RequestMapping(value = '/', produces = 'text/html')
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.web
|
||||
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy
|
||||
import org.springframework.web.context.request.NativeWebRequest
|
||||
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
|
||||
import org.springframework.web.util.UrlPathHelper
|
||||
|
||||
/**
|
||||
* Spring Initializr web configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.0
|
||||
*/
|
||||
class WebConfig extends WebMvcConfigurerAdapter {
|
||||
|
||||
static final String CURL_USER_AGENT_PREFIX = 'curl'
|
||||
|
||||
@Override
|
||||
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
|
||||
configurer.defaultContentTypeStrategy(new CommandLineContentNegotiationStrategy())
|
||||
}
|
||||
|
||||
/**
|
||||
* A command-line aware {@link ContentNegotiationStrategy} that forces the media type
|
||||
* to "text/plain" for compatible agents.
|
||||
*/
|
||||
private static class CommandLineContentNegotiationStrategy implements ContentNegotiationStrategy {
|
||||
|
||||
private final UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
@Override
|
||||
List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
|
||||
String path = urlPathHelper.getPathWithinApplication(
|
||||
request.getNativeRequest(HttpServletRequest.class));
|
||||
if (!path || !path.equals("/")) { // Only care about "/"
|
||||
return Collections.emptyList()
|
||||
}
|
||||
String userAgent = request.getHeader(HttpHeaders.USER_AGENT)
|
||||
if (userAgent) {
|
||||
if (userAgent.startsWith(CURL_USER_AGENT_PREFIX)) {
|
||||
return Collections.singletonList(MediaType.TEXT_PLAIN)
|
||||
}
|
||||
}
|
||||
return Collections.singletonList(MediaType.APPLICATION_JSON)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
48
initializr/src/main/resources/templates/cli-capabilities.txt
Normal file
48
initializr/src/main/resources/templates/cli-capabilities.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
${logo}
|
||||
:: Spring Initializr :: ${serviceUrl}
|
||||
|
||||
This service generates quickstart projects and can be customized
|
||||
in many ways; the dependencies, java version, build system or
|
||||
build structure can be namely customized easily.
|
||||
|
||||
The services uses a HAL based hypermedia format to expose a set of
|
||||
resources to interact with. If you access this root resource
|
||||
requesting application/json as media type the response will contain
|
||||
the following links:
|
||||
<% types.each {key, value -> %>
|
||||
* ${key} - ${value} <% } %>
|
||||
|
||||
The URI templates take a set of parameters to customize the result of
|
||||
a request to the linked resource.
|
||||
|
||||
+-----------------+------------------------------------------+-----------------
|
||||
| Parameter | Description | Default
|
||||
|-----------------+------------------------------------------+-----------------
|
||||
| groupId | project coordinates | ${defaults.groupId}
|
||||
| artifactId | project coordinates (infer archive name) | ${defaults.artifactId}
|
||||
| version | project version | ${defaults.version}
|
||||
| name | project name (infer application name) | ${defaults.name}
|
||||
| description | project description | ${defaults.description}
|
||||
| packageName | root package | ${defaults.packageName}
|
||||
| applicationName | application name | ${defaults.applicationName}
|
||||
| dependencies | dependency identifiers (comma separated) | none
|
||||
| type | project type | ${defaults.type}
|
||||
| packaging | project packaging | ${defaults.packaging}
|
||||
| language | programming language | ${defaults.language}
|
||||
| javaVersion | language level | ${defaults.javaVersion}
|
||||
| bootVersion | spring boot version | ${defaults.bootVersion}
|
||||
| baseDir | base directory to create in the archive | no base dir
|
||||
+-----------------+------------------------------------------+-----------------
|
||||
|
||||
The following section has a list of supported identifiers for the comma separated
|
||||
list of "dependencies".
|
||||
<% dependencies.each {key, value -> %>
|
||||
${key} - ${value} <% } %>
|
||||
|
||||
|
||||
<% if (hasExamples) { %>
|
||||
Examples:
|
||||
|
||||
${examples}
|
||||
|
||||
<% } %>
|
||||
13
initializr/src/main/resources/templates/curl-examples.txt
Normal file
13
initializr/src/main/resources/templates/curl-examples.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
To create a default demo.zip:
|
||||
\$ curl ${serviceUrl}/starter.zip -o demo.zip
|
||||
|
||||
To create a web project using Java 8:
|
||||
\$ curl ${serviceUrl}/starter.zip -d dependencies=web \\
|
||||
-d javaVersion=1.8 -o demo.zip
|
||||
|
||||
To create a web/data-jpa gradle project unpacked:
|
||||
\$ curl ${serviceUrl}/starter.tgz -d dependencies=web,data-jpa \\
|
||||
-d type=gradle-project -d baseDir=my-dir | tar -xzvf -
|
||||
|
||||
To generate a Maven POM with war packaging:
|
||||
\$ curl ${serviceUrl}/pom.xml -d packaging=war -o pom.xml
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 io.spring.initializr.test.InitializrMetadataBuilder
|
||||
import org.junit.Test
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString
|
||||
import static org.hamcrest.core.IsNot.not
|
||||
import static org.junit.Assert.assertThat
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class CommandLineHelpGeneratorTest {
|
||||
|
||||
private CommandLineHelpGenerator generator = new CommandLineHelpGenerator()
|
||||
|
||||
@Test
|
||||
void generateGenericCapabilities() {
|
||||
def metadata = InitializrMetadataBuilder.withDefaults().addDependencyGroup("test",
|
||||
createDependency('id-b', 'depB'),
|
||||
createDependency('id-a', 'depA', 'and some description')).validateAndGet()
|
||||
String content = generator.generateGenericCapabilities(metadata, "https://fake-service")
|
||||
assertThat content, containsString('id-a - depA: and some description')
|
||||
assertThat content, containsString('id-b - depB')
|
||||
assertThat content, containsString("https://fake-service")
|
||||
assertThat content, not(containsString('Examples:'))
|
||||
assertThat content, not(containsString('curl'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCapabilitiesWithTypeDescription() {
|
||||
def metadata = InitializrMetadataBuilder.withDefaults()
|
||||
.addType(new InitializrMetadata.Type(id: 'foo', name: 'foo-name', description: 'foo-desc'))
|
||||
.validateAndGet()
|
||||
String content = generator.generateGenericCapabilities(metadata, "https://fake-service")
|
||||
assertThat content, containsString('foo - foo-desc')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCurlCapabilities() {
|
||||
def metadata = InitializrMetadataBuilder.withDefaults().addDependencyGroup("test",
|
||||
createDependency('id-b', 'depB'),
|
||||
createDependency('id-a', 'depA', 'and some description')).validateAndGet()
|
||||
String content = generator.generateCurlCapabilities(metadata, "https://fake-service")
|
||||
assertThat content, containsString('id-a - depA: and some description')
|
||||
assertThat content, containsString('id-b - depB')
|
||||
assertThat content, containsString("https://fake-service")
|
||||
assertThat content, containsString('Examples:')
|
||||
assertThat content, containsString('curl')
|
||||
}
|
||||
|
||||
|
||||
private static def createDependency(String id, String name) {
|
||||
createDependency(id, name, null)
|
||||
}
|
||||
|
||||
private static def createDependency(String id, String name, String description) {
|
||||
new InitializrMetadata.Dependency(id: id, name: name, description: description)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -86,6 +86,10 @@ class InitializrMetadataBuilder {
|
||||
if (format) {
|
||||
type.tags['format'] = format
|
||||
}
|
||||
addType(type)
|
||||
}
|
||||
|
||||
InitializrMetadataBuilder addType(InitializrMetadata.Type type) {
|
||||
metadata.types.add(type)
|
||||
this
|
||||
}
|
||||
|
||||
@@ -63,6 +63,13 @@ class ProjectAssert {
|
||||
new PomAssert(file('pom.xml').text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link GradleBuildAssert} for this project.
|
||||
*/
|
||||
GradleBuildAssert gradleBuildAssert() {
|
||||
new GradleBuildAssert(file('build.gradle').text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link SourceCodeAssert} for the specified source code.
|
||||
*/
|
||||
|
||||
@@ -33,10 +33,14 @@ import org.springframework.http.HttpEntity
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
|
||||
import org.springframework.test.context.web.WebAppConfiguration
|
||||
import org.springframework.web.client.RestTemplate
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertTrue
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@@ -64,6 +68,17 @@ abstract class AbstractInitializrControllerIntegrationTests {
|
||||
restTemplate.exchange(createUrl('/'), HttpMethod.GET, new HttpEntity<Void>(headers), String).body
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the 'Content-Type' header of the specified response.
|
||||
*/
|
||||
protected void validateContentType(ResponseEntity<String> response, MediaType expected) {
|
||||
def actual = response.headers.getContentType()
|
||||
assertTrue "Non compatible media-type, expected $expected, got $actual" ,
|
||||
actual.isCompatibleWith(expected)
|
||||
assertEquals 'All text content should be UTF-8 encoded',
|
||||
'UTF-8', actual.getParameter('charset')
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ProjectAssert} for the following archive content.
|
||||
*/
|
||||
@@ -78,7 +93,40 @@ abstract class AbstractInitializrControllerIntegrationTests {
|
||||
projectAssert(content, ArchiveType.TGZ)
|
||||
}
|
||||
|
||||
ProjectAssert projectAssert(byte[] content, ArchiveType archiveType) {
|
||||
protected ProjectAssert downloadZip(String context) {
|
||||
def body = downloadArchive(context)
|
||||
zipProjectAssert(body)
|
||||
}
|
||||
|
||||
protected ProjectAssert downloadTgz(String context) {
|
||||
def body = downloadArchive(context)
|
||||
tgzProjectAssert(body)
|
||||
}
|
||||
|
||||
protected byte[] downloadArchive(String context) {
|
||||
restTemplate.getForObject(createUrl(context), byte[])
|
||||
}
|
||||
|
||||
protected ResponseEntity<String> invokeHome(String userAgentHeader, String acceptHeader) {
|
||||
execute('/', String, userAgentHeader, acceptHeader)
|
||||
}
|
||||
|
||||
protected <T> ResponseEntity<T> execute(String contextPath, Class<T> responseType,
|
||||
String userAgentHeader, String acceptHeader) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
if (userAgentHeader) {
|
||||
headers.set("User-Agent", userAgentHeader);
|
||||
}
|
||||
if (acceptHeader) {
|
||||
headers.setAccept(Collections.singletonList(MediaType.parseMediaType(acceptHeader)))
|
||||
} else {
|
||||
headers.setAccept(Collections.emptyList())
|
||||
}
|
||||
return restTemplate.exchange(createUrl(contextPath),
|
||||
HttpMethod.GET, new HttpEntity<Void>(headers), responseType)
|
||||
}
|
||||
|
||||
protected ProjectAssert projectAssert(byte[] content, ArchiveType archiveType) {
|
||||
def archiveFile = writeArchive(content)
|
||||
|
||||
def project = folder.newFolder()
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.web
|
||||
|
||||
import io.spring.initializr.test.PomAssert
|
||||
import org.junit.Test
|
||||
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
|
||||
/**
|
||||
* Validate that the "raw" HTTP commands that are described in the
|
||||
* command-line help works. If anything needs to be updated here, please
|
||||
* double check the "curl-examples.txt" as it may need an update
|
||||
* as well. This is also a good indicator of a non backward compatible
|
||||
* change.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ActiveProfiles('test-default')
|
||||
class CommandLineExampleIntegrationTests extends AbstractInitializrControllerIntegrationTests {
|
||||
|
||||
@Test
|
||||
void generateDefaultProject() {
|
||||
downloadZip('/starter.zip').isJavaProject()
|
||||
.isMavenProject().hasStaticAndTemplatesResources(false).pomAssert()
|
||||
.hasSpringBootStarterRootDependency()
|
||||
.hasSpringBootStarterDependency('test')
|
||||
.hasDependenciesCount(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWebProjectWithJava8() {
|
||||
downloadZip('/starter.zip?dependencies=web&javaVersion=1.8').isJavaProject()
|
||||
.isMavenProject().hasStaticAndTemplatesResources(true).pomAssert()
|
||||
.hasJavaVersion('1.8')
|
||||
.hasSpringBootStarterDependency('web')
|
||||
.hasSpringBootStarterDependency('test')
|
||||
.hasDependenciesCount(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWebDataJpaGradleProject() {
|
||||
downloadTgz('/starter.tgz?dependencies=web,data-jpa&type=gradle-project&baseDir=my-dir')
|
||||
.hasBaseDir('my-dir')
|
||||
.isJavaProject()
|
||||
.isGradleProject().hasStaticAndTemplatesResources(true)
|
||||
.gradleBuildAssert()
|
||||
.contains('spring-boot-starter-web')
|
||||
.contains('spring-boot-starter-data-jpa')
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateMavenPomWithWarPackaging() {
|
||||
def response = restTemplate.getForEntity(createUrl('/pom.xml?packaging=war'), String)
|
||||
PomAssert pomAssert = new PomAssert(response.body)
|
||||
pomAssert.hasPackaging('war')
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package io.spring.initializr.web
|
||||
import java.nio.charset.Charset
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import io.spring.initializr.test.ProjectAssert
|
||||
import org.json.JSONObject
|
||||
import org.junit.Test
|
||||
import org.skyscreamer.jsonassert.JSONAssert
|
||||
@@ -36,6 +35,9 @@ import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.util.StreamUtils
|
||||
import org.springframework.web.client.HttpClientErrorException
|
||||
|
||||
import static org.hamcrest.CoreMatchers.allOf
|
||||
import static org.hamcrest.CoreMatchers.containsString
|
||||
import static org.hamcrest.core.IsNot.not
|
||||
import static org.junit.Assert.*
|
||||
|
||||
/**
|
||||
@@ -132,29 +134,92 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
|
||||
|
||||
@Test
|
||||
void metadataWithNoAcceptHeader() { // rest template sets application/json by default
|
||||
ResponseEntity<String> response = getMetadata(null, '*/*')
|
||||
assertTrue response.headers.getContentType().isCompatibleWith(CURRENT_METADATA_MEDIA_TYPE)
|
||||
validateCurrentMetadata(new JSONObject(response.body))
|
||||
ResponseEntity<String> response = invokeHome(null, '*/*')
|
||||
validateCurrentMetadata(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
void metadataWithCurrentAcceptHeader() {
|
||||
ResponseEntity<String> response = getMetadata(null, 'application/vnd.initializr.v2+json')
|
||||
assertTrue response.headers.getContentType().isCompatibleWith(CURRENT_METADATA_MEDIA_TYPE)
|
||||
ResponseEntity<String> response = invokeHome(null, 'application/vnd.initializr.v2+json')
|
||||
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE)
|
||||
validateCurrentMetadata(new JSONObject(response.body))
|
||||
}
|
||||
|
||||
@Test
|
||||
void curlReceivesTextByDefault() {
|
||||
ResponseEntity<String> response = invokeHome('curl/1.2.4', "*/*")
|
||||
validateCurlHelpContent(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
void curlCanStillDownloadZipArchive() {
|
||||
ResponseEntity<byte[]> response = execute('/starter.zip', byte[], 'curl/1.2.4', "*/*")
|
||||
zipProjectAssert(response.body).isMavenProject().isJavaProject()
|
||||
}
|
||||
|
||||
@Test
|
||||
void curlCanStillDownloadTgzArchive() {
|
||||
ResponseEntity<byte[]> response = execute('/starter.tgz', byte[], 'curl/1.2.4', "*/*")
|
||||
tgzProjectAssert(response.body).isMavenProject().isJavaProject()
|
||||
}
|
||||
|
||||
@Test // make sure curl can still receive metadata with json
|
||||
void curlWithAcceptHeaderJson() {
|
||||
ResponseEntity<String> response = invokeHome('curl/1.2.4', "application/json")
|
||||
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE)
|
||||
validateCurrentMetadata(new JSONObject(response.body))
|
||||
}
|
||||
|
||||
@Test
|
||||
void curlWithAcceptHeaderTextPlain() {
|
||||
ResponseEntity<String> response = invokeHome('curl/1.2.4', "text/plain")
|
||||
validateCurlHelpContent(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownAgentReceivesJsonByDefault() {
|
||||
ResponseEntity<String> response = invokeHome('foo/1.0', "*/*")
|
||||
validateCurrentMetadata(response)
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownCliWithTextPlain() {
|
||||
ResponseEntity<String> response = invokeHome(null, "text/plain")
|
||||
validateGenericHelpContent(response)
|
||||
}
|
||||
|
||||
@Test // Test that the current output is exactly what we expect
|
||||
void validateCurrentProjectMetadata() {
|
||||
def json = getMetadataJson()
|
||||
validateCurrentMetadata(json)
|
||||
}
|
||||
|
||||
private void validateCurrentMetadata(ResponseEntity<String> response) {
|
||||
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE)
|
||||
validateCurrentMetadata(new JSONObject(response.body))
|
||||
}
|
||||
|
||||
private void validateCurrentMetadata(JSONObject json) {
|
||||
def expected = readJson('2.0.0')
|
||||
JSONAssert.assertEquals(expected, json, JSONCompareMode.STRICT)
|
||||
}
|
||||
|
||||
private void validateCurlHelpContent(ResponseEntity<String> response) {
|
||||
validateContentType(response, MediaType.TEXT_PLAIN)
|
||||
assertThat(response.body, allOf(
|
||||
containsString("Spring Initializr"),
|
||||
containsString('Examples:'),
|
||||
containsString("curl")))
|
||||
}
|
||||
|
||||
private void validateGenericHelpContent(ResponseEntity<String> response) {
|
||||
validateContentType(response, MediaType.TEXT_PLAIN)
|
||||
assertThat(response.body, allOf(
|
||||
containsString("Spring Initializr"),
|
||||
not(containsString('Examples:')),
|
||||
not(containsString("curl"))))
|
||||
}
|
||||
|
||||
@Test // Test that the current code complies exactly with 1.1.0
|
||||
void validateProjectMetadata110() {
|
||||
JSONObject json = getMetadataJson("SpringBootCli/1.2.0.RC1", null)
|
||||
@@ -208,7 +273,7 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
|
||||
@Test
|
||||
void missingDependencyProperException() {
|
||||
try {
|
||||
invoke('/starter.zip?style=foo:bar')
|
||||
downloadArchive('/starter.zip?style=foo:bar')
|
||||
} catch (HttpClientErrorException ex) {
|
||||
def error = parseJson(ex.responseBodyAsString)
|
||||
assertEquals HttpStatus.BAD_REQUEST, ex.getStatusCode()
|
||||
@@ -236,7 +301,7 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
|
||||
|
||||
@Test
|
||||
void homeIsJson() {
|
||||
def body = restTemplate.getForObject(createUrl('/'), String)
|
||||
def body = invokeHome(null, null).body
|
||||
assertTrue("Wrong body:\n$body", body.contains('"dependencies"'))
|
||||
}
|
||||
|
||||
@@ -293,37 +358,10 @@ class MainControllerIntegrationTests extends AbstractInitializrControllerIntegra
|
||||
}
|
||||
|
||||
private JSONObject getMetadataJson(String userAgentHeader, String acceptHeader) {
|
||||
String json = getMetadata(userAgentHeader, acceptHeader).body
|
||||
String json = invokeHome(userAgentHeader, acceptHeader).body
|
||||
return new JSONObject(json)
|
||||
}
|
||||
|
||||
private ResponseEntity<String> getMetadata(String userAgentHeader, String acceptHeader) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
if (userAgentHeader) {
|
||||
headers.set("User-Agent", userAgentHeader);
|
||||
}
|
||||
if (acceptHeader) {
|
||||
headers.setAccept(Collections.singletonList(MediaType.parseMediaType(acceptHeader)))
|
||||
}
|
||||
return restTemplate.exchange(createUrl('/'),
|
||||
HttpMethod.GET, new HttpEntity<Void>(headers), String.class)
|
||||
}
|
||||
|
||||
|
||||
private byte[] invoke(String context) {
|
||||
restTemplate.getForObject(createUrl(context), byte[])
|
||||
}
|
||||
|
||||
private ProjectAssert downloadZip(String context) {
|
||||
def body = invoke(context)
|
||||
zipProjectAssert(body)
|
||||
}
|
||||
|
||||
private ProjectAssert downloadTgz(String context) {
|
||||
def body = invoke(context)
|
||||
tgzProjectAssert(body)
|
||||
}
|
||||
|
||||
private JSONObject readJson(String version) {
|
||||
def resource = new ClassPathResource("metadata/test-default-$version" + ".json")
|
||||
def stream = resource.inputStream
|
||||
|
||||
Reference in New Issue
Block a user