mirror of
https://gitee.com/dcren/initializr.git
synced 2026-02-25 21:22:58 +08:00
Migrate initializr-actuator to Java
This commit is contained in:
committed by
Stephane Nicoll
parent
fa485e3690
commit
ad6430b92a
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@
|
||||
.classpath
|
||||
.project
|
||||
.settings
|
||||
.gitignore
|
||||
.idea
|
||||
.factorypath
|
||||
*.iml
|
||||
|
||||
@@ -75,6 +75,11 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.info
|
||||
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider
|
||||
|
||||
import org.springframework.boot.actuate.info.Info
|
||||
import org.springframework.boot.actuate.info.InfoContributor
|
||||
|
||||
/**
|
||||
* An {@link InfoContributor} that exposes the actual ranges used by each bom
|
||||
* defined in the project.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BomRangesInfoContributor implements InfoContributor {
|
||||
|
||||
private final InitializrMetadataProvider metadataProvider
|
||||
|
||||
BomRangesInfoContributor(InitializrMetadataProvider metadataProvider) {
|
||||
this.metadataProvider = metadataProvider
|
||||
}
|
||||
|
||||
@Override
|
||||
void contribute(Info.Builder builder) {
|
||||
def details = [:]
|
||||
metadataProvider.get().configuration.env.boms.each { k, v ->
|
||||
if (v.mappings) {
|
||||
def bom = [:]
|
||||
v.mappings.each {
|
||||
String requirement = "Spring Boot ${it.determineVersionRangeRequirement()}"
|
||||
bom[it.version] = requirement
|
||||
}
|
||||
details[k] = bom
|
||||
}
|
||||
}
|
||||
if (details) {
|
||||
builder.withDetail('bom-ranges', details)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.metric
|
||||
|
||||
import io.spring.initializr.generator.ProjectFailedEvent
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent
|
||||
import io.spring.initializr.generator.ProjectRequest
|
||||
import io.spring.initializr.util.Agent
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.actuate.metrics.CounterService
|
||||
import org.springframework.context.event.EventListener
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
/**
|
||||
* A {@link ProjectGeneratedEvent} listener that uses a {@link CounterService} to update
|
||||
* various project related metrics.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ProjectGenerationMetricsListener {
|
||||
|
||||
private final CounterService counterService
|
||||
|
||||
@Autowired
|
||||
ProjectGenerationMetricsListener(CounterService counterService) {
|
||||
this.counterService = counterService
|
||||
}
|
||||
|
||||
@EventListener
|
||||
void onGeneratedProject(ProjectGeneratedEvent event) {
|
||||
handleProjectRequest(event.projectRequest)
|
||||
}
|
||||
|
||||
@EventListener
|
||||
void onFailedProject(ProjectFailedEvent event) {
|
||||
handleProjectRequest(event.projectRequest)
|
||||
increment(key('failures'))
|
||||
}
|
||||
|
||||
protected void handleProjectRequest(ProjectRequest request) {
|
||||
increment(key('requests')) // Total number of requests
|
||||
handleDependencies(request)
|
||||
handleType(request)
|
||||
handleJavaVersion(request)
|
||||
handlePackaging(request)
|
||||
handleLanguage(request)
|
||||
handleBootVersion(request)
|
||||
handleUserAgent(request)
|
||||
}
|
||||
|
||||
protected void handleDependencies(ProjectRequest request) {
|
||||
request.resolvedDependencies.each {
|
||||
if (!ProjectRequest.DEFAULT_STARTER.equals(it.id)) {
|
||||
def id = sanitize(it.id)
|
||||
increment(key("dependency.$id"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleType(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.type)) {
|
||||
def type = sanitize(request.type)
|
||||
increment(key("type.$type"))
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleJavaVersion(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.javaVersion)) {
|
||||
def javaVersion = sanitize(request.javaVersion)
|
||||
increment(key("java_version.$javaVersion"))
|
||||
}
|
||||
}
|
||||
|
||||
protected void handlePackaging(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.packaging)) {
|
||||
def packaging = sanitize(request.packaging)
|
||||
increment(key("packaging.$packaging"))
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleLanguage(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.language)) {
|
||||
def language = sanitize(request.language)
|
||||
increment(key("language.$language"))
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleBootVersion(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.bootVersion)) {
|
||||
def bootVersion = sanitize(request.bootVersion)
|
||||
increment(key("boot_version.$bootVersion"))
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleUserAgent(ProjectRequest request) {
|
||||
String userAgent = request.parameters['user-agent']
|
||||
if (userAgent) {
|
||||
Agent agent = Agent.fromUserAgent(userAgent)
|
||||
if (agent) {
|
||||
increment(key("client_id.$agent.id.id"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void increment(String key) {
|
||||
counterService.increment(key)
|
||||
}
|
||||
|
||||
protected String key(String part) {
|
||||
"initializr.$part"
|
||||
}
|
||||
|
||||
protected String sanitize(String s) {
|
||||
s.replace('.', '_')
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.stat
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import groovy.util.logging.Slf4j
|
||||
import io.spring.initializr.generator.ProjectRequestEvent
|
||||
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder
|
||||
import org.springframework.context.event.EventListener
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.RequestEntity
|
||||
import org.springframework.retry.RetryCallback
|
||||
import org.springframework.retry.RetryContext
|
||||
import org.springframework.retry.support.RetryTemplate
|
||||
import org.springframework.scheduling.annotation.Async
|
||||
import org.springframework.web.client.RestTemplate
|
||||
|
||||
/**
|
||||
* Publish stats for each project generated to an Elastic index.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Slf4j
|
||||
class ProjectGenerationStatPublisher {
|
||||
|
||||
private final ProjectRequestDocumentFactory documentFactory
|
||||
private final StatsProperties statsProperties
|
||||
private final ObjectMapper objectMapper
|
||||
private final RestTemplate restTemplate
|
||||
private final RetryTemplate retryTemplate
|
||||
|
||||
ProjectGenerationStatPublisher(ProjectRequestDocumentFactory documentFactory,
|
||||
StatsProperties statsProperties,
|
||||
RetryTemplate retryTemplate) {
|
||||
this.documentFactory = documentFactory
|
||||
this.statsProperties = statsProperties
|
||||
this.objectMapper = createObjectMapper()
|
||||
this.restTemplate = new RestTemplateBuilder().basicAuthorization(
|
||||
statsProperties.elastic.username, statsProperties.elastic.password).build()
|
||||
this.retryTemplate = retryTemplate
|
||||
}
|
||||
|
||||
@EventListener
|
||||
@Async
|
||||
void handleEvent(ProjectRequestEvent event) {
|
||||
String json = null
|
||||
try {
|
||||
ProjectRequestDocument document = documentFactory.createDocument(event)
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Publishing $document")
|
||||
}
|
||||
json = toJson(document)
|
||||
|
||||
RequestEntity<String> request = RequestEntity
|
||||
.post(this.statsProperties.elastic.entityUrl)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(json)
|
||||
|
||||
this.retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
|
||||
@Override
|
||||
Void doWithRetry(RetryContext context) {
|
||||
restTemplate.exchange(request, String)
|
||||
return null
|
||||
}
|
||||
})
|
||||
} catch (Exception ex) {
|
||||
log.warn(String.format(
|
||||
"Failed to publish stat to index, document follows %n%n%s%n", json), ex)
|
||||
}
|
||||
}
|
||||
|
||||
private String toJson(ProjectRequestDocument stats) {
|
||||
this.objectMapper.writeValueAsString(stats)
|
||||
}
|
||||
|
||||
private static ObjectMapper createObjectMapper() {
|
||||
def mapper = new ObjectMapper()
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
mapper
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.stat
|
||||
|
||||
import groovy.transform.ToString
|
||||
|
||||
/**
|
||||
* Define the statistics of a project generation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ToString(ignoreNulls = true, includePackage = false, includeNames = true)
|
||||
class ProjectRequestDocument {
|
||||
|
||||
long generationTimestamp
|
||||
|
||||
String requestIp
|
||||
String requestIpv4
|
||||
String requestCountry
|
||||
String clientId
|
||||
String clientVersion
|
||||
|
||||
String groupId
|
||||
String artifactId
|
||||
String packageName
|
||||
String bootVersion
|
||||
String javaVersion
|
||||
String language
|
||||
String packaging
|
||||
String type
|
||||
final List<String> dependencies = []
|
||||
|
||||
String errorMessage
|
||||
boolean invalid
|
||||
boolean invalidJavaVersion
|
||||
boolean invalidLanguage
|
||||
boolean invalidPackaging
|
||||
boolean invalidType
|
||||
final List<String> invalidDependencies = []
|
||||
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.stat
|
||||
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import io.spring.initializr.generator.ProjectFailedEvent
|
||||
import io.spring.initializr.generator.ProjectRequest
|
||||
import io.spring.initializr.generator.ProjectRequestEvent
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider
|
||||
import io.spring.initializr.util.Agent
|
||||
|
||||
/**
|
||||
* Create {@link ProjectRequestDocument} instances.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ProjectRequestDocumentFactory {
|
||||
|
||||
private static final IP_PATTERN = Pattern.compile("[0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*")
|
||||
|
||||
private final InitializrMetadataProvider metadataProvider
|
||||
|
||||
ProjectRequestDocumentFactory(InitializrMetadataProvider metadataProvider) {
|
||||
this.metadataProvider = metadataProvider
|
||||
}
|
||||
|
||||
ProjectRequestDocument createDocument(ProjectRequestEvent event) {
|
||||
def metadata = metadataProvider.get()
|
||||
def request = event.projectRequest
|
||||
|
||||
ProjectRequestDocument document = new ProjectRequestDocument()
|
||||
document.generationTimestamp = event.timestamp
|
||||
|
||||
handleCloudFlareHeaders(request, document)
|
||||
def candidate = request.parameters['x-forwarded-for']
|
||||
if (!document.requestIp && candidate) {
|
||||
document.requestIp = candidate
|
||||
document.requestIpv4 = extractIpv4(candidate)
|
||||
}
|
||||
|
||||
Agent agent = extractAgentInformation(request)
|
||||
if (agent) {
|
||||
document.clientId = agent.id.id
|
||||
document.clientVersion = agent.version
|
||||
}
|
||||
|
||||
document.groupId = request.groupId
|
||||
document.artifactId = request.artifactId
|
||||
document.packageName = request.packageName
|
||||
document.bootVersion = request.bootVersion
|
||||
|
||||
document.javaVersion = request.javaVersion
|
||||
if (request.javaVersion && !metadata.javaVersions.get(request.javaVersion)) {
|
||||
document.invalid = true
|
||||
document.invalidJavaVersion = true
|
||||
}
|
||||
|
||||
document.language = request.language
|
||||
if (request.language && !metadata.languages.get(request.language)) {
|
||||
document.invalid = true
|
||||
document.invalidLanguage = true
|
||||
}
|
||||
|
||||
document.packaging = request.packaging
|
||||
if (request.packaging && !metadata.packagings.get(request.packaging)) {
|
||||
document.invalid = true
|
||||
document.invalidPackaging = true
|
||||
}
|
||||
|
||||
document.type = request.type
|
||||
if (request.type && !metadata.types.get(request.type)) {
|
||||
document.invalid = true
|
||||
document.invalidType = true
|
||||
}
|
||||
|
||||
// Let's not rely on the resolved dependencies here
|
||||
def dependencies = []
|
||||
dependencies.addAll(request.style)
|
||||
dependencies.addAll(request.dependencies)
|
||||
dependencies.each { id ->
|
||||
if (metadata.dependencies.get(id)) {
|
||||
document.dependencies << id
|
||||
} else {
|
||||
document.invalid = true
|
||||
document.invalidDependencies << id
|
||||
}
|
||||
}
|
||||
|
||||
// Let's make sure that the document is flagged as invalid no matter what
|
||||
if (event instanceof ProjectFailedEvent) {
|
||||
document.invalid = true
|
||||
if (event.cause) {
|
||||
document.errorMessage = event.cause.message
|
||||
}
|
||||
}
|
||||
|
||||
document
|
||||
}
|
||||
|
||||
private static void handleCloudFlareHeaders(ProjectRequest request, ProjectRequestDocument document) {
|
||||
def candidate = request.parameters['cf-connecting-ip']
|
||||
if (candidate) {
|
||||
document.requestIp = candidate
|
||||
document.requestIpv4 = extractIpv4(candidate)
|
||||
}
|
||||
String country = request.parameters['cf-ipcountry']
|
||||
if (country && !country.toLowerCase().equals('xx')) {
|
||||
document.requestCountry = country
|
||||
}
|
||||
}
|
||||
|
||||
private static Agent extractAgentInformation(ProjectRequest request) {
|
||||
String userAgent = request.parameters['user-agent']
|
||||
if (userAgent) {
|
||||
return Agent.fromUserAgent(userAgent)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private static String extractIpv4(def candidate) {
|
||||
if (candidate) {
|
||||
Matcher matcher = IP_PATTERN.matcher(candidate)
|
||||
if (matcher.find()) {
|
||||
return matcher.group()
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.stat
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
/**
|
||||
* Statistics-related properties.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ConfigurationProperties("initializr.stats")
|
||||
class StatsProperties {
|
||||
|
||||
final Elastic elastic = new Elastic()
|
||||
|
||||
static final class Elastic {
|
||||
|
||||
/**
|
||||
* Elastic service uri.
|
||||
*/
|
||||
String uri
|
||||
|
||||
/**
|
||||
* Elastic service username.
|
||||
*/
|
||||
String username
|
||||
|
||||
/**
|
||||
* Elastic service password
|
||||
*/
|
||||
String password
|
||||
|
||||
/**
|
||||
* Name of the index.
|
||||
*/
|
||||
String indexName = 'initializr'
|
||||
|
||||
/**
|
||||
* Name of the entity to use to publish stats.
|
||||
*/
|
||||
String entityName = 'request'
|
||||
|
||||
/**
|
||||
* Number of attempts before giving up.
|
||||
*/
|
||||
int maxAttempts = 3
|
||||
|
||||
void setUri(String uri) {
|
||||
this.uri = cleanUri(uri)
|
||||
}
|
||||
|
||||
URI getEntityUrl() {
|
||||
def string = "$uri/$indexName/$entityName"
|
||||
new URI(string)
|
||||
}
|
||||
|
||||
private static String cleanUri(String contextPath) {
|
||||
if (StringUtils.hasText(contextPath) && contextPath.endsWith("/")) {
|
||||
return contextPath.substring(0, contextPath.length() - 1)
|
||||
}
|
||||
return contextPath
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.actuate.autoconfigure
|
||||
package io.spring.initializr.actuate.autoconfigure;
|
||||
|
||||
import io.spring.initializr.actuate.info.BomRangesInfoContributor
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import io.spring.initializr.actuate.info.BomRangesInfoContributor;
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
@@ -29,12 +29,12 @@ import org.springframework.context.annotation.Configuration
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Configuration
|
||||
class InitializrActuatorEndpointsAutoConfiguration {
|
||||
public class InitializrActuatorEndpointsAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
BomRangesInfoContributor bomRangesInfoContributor(
|
||||
InitializrMetadataProvider metadataProvider) {
|
||||
return new BomRangesInfoContributor(metadataProvider)
|
||||
return new BomRangesInfoContributor(metadataProvider);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,27 +14,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.actuate.autoconfigure
|
||||
package io.spring.initializr.actuate.autoconfigure;
|
||||
|
||||
import io.spring.initializr.actuate.metric.ProjectGenerationMetricsListener
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.ExportMetricWriter;
|
||||
import org.springframework.boot.actuate.autoconfigure.MetricExportAutoConfiguration;
|
||||
import org.springframework.boot.actuate.metrics.CounterService;
|
||||
import org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository;
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.actuate.autoconfigure.ExportMetricWriter
|
||||
import org.springframework.boot.actuate.autoconfigure.MetricExportAutoConfiguration
|
||||
import org.springframework.boot.actuate.metrics.CounterService
|
||||
import org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.context.ApplicationContext
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory
|
||||
import org.springframework.scheduling.annotation.EnableScheduling
|
||||
import org.springframework.util.ObjectUtils
|
||||
import io.spring.initializr.actuate.metric.ProjectGenerationMetricsListener;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
@@ -43,39 +43,38 @@ import org.springframework.util.ObjectUtils
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter([RedisAutoConfiguration, MetricExportAutoConfiguration])
|
||||
class InitializrMetricsConfiguration {
|
||||
@AutoConfigureAfter({ RedisAutoConfiguration.class, MetricExportAutoConfiguration.class })
|
||||
public class InitializrMetricsConfiguration {
|
||||
|
||||
@Bean
|
||||
ProjectGenerationMetricsListener metricsListener(CounterService counterService) {
|
||||
new ProjectGenerationMetricsListener(counterService)
|
||||
return new ProjectGenerationMetricsListener(counterService);
|
||||
}
|
||||
|
||||
@ConditionalOnBean(RedisConnectionFactory)
|
||||
@ConditionalOnProperty(value = 'spring.metrics.export.enabled')
|
||||
@ConditionalOnBean(RedisConnectionFactory.class)
|
||||
@ConditionalOnProperty(value = "spring.metrics.export.enabled")
|
||||
@EnableScheduling
|
||||
@EnableConfigurationProperties(MetricsProperties)
|
||||
@EnableConfigurationProperties(MetricsProperties.class)
|
||||
@Configuration
|
||||
public static class MetricsExportConfiguration {
|
||||
|
||||
@Autowired
|
||||
RedisConnectionFactory connectionFactory
|
||||
RedisConnectionFactory connectionFactory;
|
||||
|
||||
@Autowired
|
||||
MetricsProperties metrics
|
||||
MetricsProperties metrics;
|
||||
|
||||
@Autowired
|
||||
ApplicationContext context
|
||||
ApplicationContext context;
|
||||
|
||||
@Bean
|
||||
@ExportMetricWriter
|
||||
MetricWriter writer() {
|
||||
new RedisMetricRepository(connectionFactory,
|
||||
metrics.prefix + metrics.getId(context.getId()) + '.'
|
||||
+ ObjectUtils.getIdentityHexString(context) + '.',
|
||||
metrics.key)
|
||||
return new RedisMetricRepository(connectionFactory,
|
||||
metrics.getPrefix() + metrics.getId(context.getId()) + "."
|
||||
+ ObjectUtils.getIdentityHexString(context) + ".",
|
||||
metrics.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -14,22 +14,24 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.actuate.autoconfigure
|
||||
package io.spring.initializr.actuate.autoconfigure;
|
||||
|
||||
import io.spring.initializr.actuate.stat.ProjectGenerationStatPublisher
|
||||
import io.spring.initializr.actuate.stat.ProjectRequestDocumentFactory
|
||||
import io.spring.initializr.actuate.stat.StatsProperties
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.retry.backoff.ExponentialBackOffPolicy
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy
|
||||
import org.springframework.retry.support.RetryTemplate
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
|
||||
import io.spring.initializr.actuate.stat.ProjectGenerationStatPublisher;
|
||||
import io.spring.initializr.actuate.stat.ProjectRequestDocumentFactory;
|
||||
import io.spring.initializr.actuate.stat.StatsProperties;
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
@@ -38,29 +40,34 @@ import org.springframework.retry.support.RetryTemplate
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(StatsProperties)
|
||||
@ConditionalOnProperty('initializr.stats.elastic.uri')
|
||||
@EnableConfigurationProperties(StatsProperties.class)
|
||||
@ConditionalOnProperty("initializr.stats.elastic.uri")
|
||||
class InitializrStatsAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private StatsProperties statsProperties
|
||||
private StatsProperties statsProperties;
|
||||
|
||||
@Bean
|
||||
ProjectGenerationStatPublisher projectRequestStatHandler(InitializrMetadataProvider provider) {
|
||||
new ProjectGenerationStatPublisher(new ProjectRequestDocumentFactory(provider),
|
||||
statsProperties, statsRetryTemplate())
|
||||
ProjectGenerationStatPublisher projectRequestStatHandler(
|
||||
InitializrMetadataProvider provider) {
|
||||
return new ProjectGenerationStatPublisher(
|
||||
new ProjectRequestDocumentFactory(provider), statsProperties,
|
||||
statsRetryTemplate());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "statsRetryTemplate")
|
||||
RetryTemplate statsRetryTemplate() {
|
||||
RetryTemplate retryTemplate = new RetryTemplate()
|
||||
def backOffPolicy = new ExponentialBackOffPolicy(initialInterval: 3000L, multiplier: 3)
|
||||
def retryPolicy = new SimpleRetryPolicy(statsProperties.elastic.maxAttempts, Collections
|
||||
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true))
|
||||
retryTemplate.setBackOffPolicy(backOffPolicy)
|
||||
retryTemplate.setRetryPolicy(retryPolicy)
|
||||
retryTemplate
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
|
||||
backOffPolicy.setInitialInterval(3000L);
|
||||
backOffPolicy.setMultiplier(3);
|
||||
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(
|
||||
statsProperties.getElastic().getMaxAttempts(), Collections
|
||||
.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true));
|
||||
retryTemplate.setBackOffPolicy(backOffPolicy);
|
||||
retryTemplate.setRetryPolicy(retryPolicy);
|
||||
return retryTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,53 +14,82 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.actuate.autoconfigure
|
||||
package io.spring.initializr.actuate.autoconfigure;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Metrics-related configuration.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties('initializr.metrics')
|
||||
class MetricsProperties {
|
||||
@ConfigurationProperties("initializr.metrics")
|
||||
public class MetricsProperties {
|
||||
|
||||
/**
|
||||
* Prefix for redis keys holding metrics in data store.
|
||||
*/
|
||||
String prefix = 'spring.metrics.collector.'
|
||||
private String prefix = "spring.metrics.collector.";
|
||||
|
||||
/**
|
||||
* Redis key holding index to metrics keys in data store.
|
||||
*/
|
||||
String key = 'keys.spring.metrics.collector'
|
||||
private String key = "keys.spring.metrics.collector";
|
||||
|
||||
/**
|
||||
* Identifier for application in metrics keys. Keys will be exported in the form
|
||||
* '[id].[hex].[name]' (where '[id]' is this value, '[hex]' is unique per application
|
||||
* context, and '[name]' is the "natural" name for the metric.
|
||||
* "[id].[hex].[name]" (where "[id]" is this value, "[hex]" is unique per application
|
||||
* context, and "[name]" is the "natural" name for the metric.
|
||||
*/
|
||||
@Value('${spring.application.name:${vcap.application.name:application}}')
|
||||
String id
|
||||
@Value("${spring.application.name:${vcap.application.name:application}}")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* The rate (in milliseconds) at which metrics are exported to Redis. If the value is
|
||||
* <=0 then the export is disabled.
|
||||
*/
|
||||
@Value('${spring.metrics.export.default.delayMillis:5000}')
|
||||
long rateMillis = 5000L
|
||||
@Value("${spring.metrics.export.default.delayMillis:5000}")
|
||||
private long rateMillis = 5000L;
|
||||
|
||||
String getPrefix() {
|
||||
if (prefix.endsWith('.')) {
|
||||
return prefix
|
||||
if (prefix.endsWith(".")) {
|
||||
return prefix;
|
||||
}
|
||||
prefix + '.'
|
||||
return prefix + ".";
|
||||
}
|
||||
|
||||
String getId(String defaultValue) {
|
||||
if (id) return id
|
||||
defaultValue
|
||||
if (StringUtils.hasText(id)) return id;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getRateMillis() {
|
||||
return rateMillis;
|
||||
}
|
||||
|
||||
public void setRateMillis(long rateMillis) {
|
||||
this.rateMillis = rateMillis;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.info;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.info.Info;
|
||||
import org.springframework.boot.actuate.info.InfoContributor;
|
||||
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider;
|
||||
|
||||
/**
|
||||
* An {@link InfoContributor} that exposes the actual ranges used by each bom
|
||||
* defined in the project.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class BomRangesInfoContributor implements InfoContributor {
|
||||
|
||||
private final InitializrMetadataProvider metadataProvider;
|
||||
|
||||
public BomRangesInfoContributor(InitializrMetadataProvider metadataProvider) {
|
||||
this.metadataProvider = metadataProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contribute(Info.Builder builder) {
|
||||
Map<String, Object> details = new LinkedHashMap<>();
|
||||
metadataProvider.get().getConfiguration().getEnv().getBoms().forEach( (k, v) -> {
|
||||
if (v.getMappings()!=null && !v.getMappings().isEmpty()) {
|
||||
Map<String, Object> bom = new LinkedHashMap<>();
|
||||
v.getMappings().forEach(it -> {
|
||||
String requirement = "Spring Boot " + it.determineVersionRangeRequirement();
|
||||
bom.put(it.getVersion(), requirement);
|
||||
});
|
||||
details.put(k, bom);
|
||||
}
|
||||
});
|
||||
if (!details.isEmpty()) {
|
||||
builder.withDetail("bom-ranges", details);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.metric;
|
||||
|
||||
import io.spring.initializr.generator.ProjectFailedEvent;
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent;
|
||||
import io.spring.initializr.generator.ProjectRequest;
|
||||
import io.spring.initializr.metadata.Dependency;
|
||||
import io.spring.initializr.util.Agent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.metrics.CounterService;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link ProjectGeneratedEvent} listener that uses a {@link CounterService} to update
|
||||
* various project related metrics.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ProjectGenerationMetricsListener {
|
||||
|
||||
private final CounterService counterService;
|
||||
|
||||
@Autowired
|
||||
public ProjectGenerationMetricsListener(CounterService counterService) {
|
||||
this.counterService = counterService;
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onGeneratedProject(ProjectGeneratedEvent event) {
|
||||
handleProjectRequest(event.getProjectRequest());
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void onFailedProject(ProjectFailedEvent event) {
|
||||
handleProjectRequest(event.getProjectRequest());
|
||||
increment(key("failures"));
|
||||
}
|
||||
|
||||
protected void handleProjectRequest(ProjectRequest request) {
|
||||
increment(key("requests"));// Total number of requests
|
||||
handleDependencies(request);
|
||||
handleType(request);
|
||||
handleJavaVersion(request);
|
||||
handlePackaging(request);
|
||||
handleLanguage(request);
|
||||
handleBootVersion(request);
|
||||
handleUserAgent(request);
|
||||
}
|
||||
|
||||
protected void handleDependencies(ProjectRequest request) {
|
||||
List<Dependency> dependencies = request.getResolvedDependencies();
|
||||
if (dependencies != null) {
|
||||
dependencies.forEach(it -> {
|
||||
if (!ProjectRequest.DEFAULT_STARTER.equals(it.getId())) {
|
||||
String id = sanitize(it.getId());
|
||||
increment(key("dependency." + id));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleType(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.getType())) {
|
||||
String type = sanitize(request.getType());
|
||||
increment(key("type." + type));
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleJavaVersion(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.getJavaVersion())) {
|
||||
String javaVersion = sanitize(request.getJavaVersion());
|
||||
increment(key("java_version." + javaVersion));
|
||||
}
|
||||
}
|
||||
|
||||
protected void handlePackaging(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.getPackaging())) {
|
||||
String packaging = sanitize(request.getPackaging());
|
||||
increment(key("packaging." + packaging));
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleLanguage(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.getLanguage())) {
|
||||
String language = sanitize(request.getLanguage());
|
||||
increment(key("language." + language));
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleBootVersion(ProjectRequest request) {
|
||||
if (StringUtils.hasText(request.getBootVersion())) {
|
||||
String bootVersion = sanitize(request.getBootVersion());
|
||||
increment(key("boot_version." + bootVersion));
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleUserAgent(ProjectRequest request) {
|
||||
String userAgent = (String) request.getParameters().get("user-agent");
|
||||
if (userAgent != null) {
|
||||
Agent agent = Agent.fromUserAgent(userAgent);
|
||||
if (agent != null) {
|
||||
increment(key("client_id." + agent.getId().getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void increment(String key) {
|
||||
counterService.increment(key);
|
||||
}
|
||||
|
||||
protected String key(String part) {
|
||||
return "initializr." + part;
|
||||
}
|
||||
|
||||
protected String sanitize(String s) {
|
||||
return s.replace(".", "_");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.stat;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.spring.initializr.generator.ProjectRequestEvent;
|
||||
|
||||
/**
|
||||
* Publish stats for each project generated to an Elastic index.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ProjectGenerationStatPublisher {
|
||||
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(ProjectGenerationStatPublisher.class);
|
||||
|
||||
private final ProjectRequestDocumentFactory documentFactory;
|
||||
private final StatsProperties statsProperties;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final RestTemplate restTemplate;
|
||||
private final RetryTemplate retryTemplate;
|
||||
|
||||
public ProjectGenerationStatPublisher(ProjectRequestDocumentFactory documentFactory,
|
||||
StatsProperties statsProperties, RetryTemplate retryTemplate) {
|
||||
this.documentFactory = documentFactory;
|
||||
this.statsProperties = statsProperties;
|
||||
this.objectMapper = createObjectMapper();
|
||||
this.restTemplate = new RestTemplateBuilder()
|
||||
.basicAuthorization(statsProperties.getElastic().getUsername(),
|
||||
statsProperties.getElastic().getPassword())
|
||||
.build();
|
||||
this.retryTemplate = retryTemplate;
|
||||
}
|
||||
|
||||
@EventListener
|
||||
@Async
|
||||
public void handleEvent(ProjectRequestEvent event) {
|
||||
String json = null;
|
||||
try {
|
||||
ProjectRequestDocument document = documentFactory.createDocument(event);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Publishing $document");
|
||||
}
|
||||
json = toJson(document);
|
||||
|
||||
RequestEntity<String> request = RequestEntity
|
||||
.post(this.statsProperties.getElastic().getEntityUrl())
|
||||
.contentType(MediaType.APPLICATION_JSON).body(json);
|
||||
|
||||
this.retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
|
||||
@Override
|
||||
public Void doWithRetry(RetryContext context) {
|
||||
restTemplate.exchange(request, String.class);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
log.warn(String.format(
|
||||
"Failed to publish stat to index, document follows %n%n%s%n", json),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String toJson(ProjectRequestDocument stats) {
|
||||
try {
|
||||
return this.objectMapper.writeValueAsString(stats);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new IllegalStateException("Cannot convert to JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static ObjectMapper createObjectMapper() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
protected RestTemplate getRestTemplate() {
|
||||
return this.restTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.stat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Define the statistics of a project generation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ProjectRequestDocument {
|
||||
|
||||
private long generationTimestamp;
|
||||
|
||||
private String requestIp;
|
||||
private String requestIpv4;
|
||||
private String requestCountry;
|
||||
private String clientId;
|
||||
private String clientVersion;
|
||||
|
||||
private String groupId;
|
||||
private String artifactId;
|
||||
private String packageName;
|
||||
private String bootVersion;
|
||||
private String javaVersion;
|
||||
private String language;
|
||||
private String packaging;
|
||||
private String type;
|
||||
private final List<String> dependencies = new ArrayList<>();
|
||||
|
||||
private String errorMessage;
|
||||
private boolean invalid;
|
||||
private boolean invalidJavaVersion;
|
||||
private boolean invalidLanguage;
|
||||
private boolean invalidPackaging;
|
||||
private boolean invalidType;
|
||||
private final List<String> invalidDependencies = new ArrayList<>();
|
||||
|
||||
public long getGenerationTimestamp() {
|
||||
return generationTimestamp;
|
||||
}
|
||||
|
||||
public void setGenerationTimestamp(long generationTimestamp) {
|
||||
this.generationTimestamp = generationTimestamp;
|
||||
}
|
||||
|
||||
public String getRequestIp() {
|
||||
return requestIp;
|
||||
}
|
||||
|
||||
public void setRequestIp(String requestIp) {
|
||||
this.requestIp = requestIp;
|
||||
}
|
||||
|
||||
public String getRequestIpv4() {
|
||||
return requestIpv4;
|
||||
}
|
||||
|
||||
public void setRequestIpv4(String requestIpv4) {
|
||||
this.requestIpv4 = requestIpv4;
|
||||
}
|
||||
|
||||
public String getRequestCountry() {
|
||||
return requestCountry;
|
||||
}
|
||||
|
||||
public void setRequestCountry(String requestCountry) {
|
||||
this.requestCountry = requestCountry;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientVersion() {
|
||||
return clientVersion;
|
||||
}
|
||||
|
||||
public void setClientVersion(String clientVersion) {
|
||||
this.clientVersion = clientVersion;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
public void setArtifactId(String artifactId) {
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public String getBootVersion() {
|
||||
return bootVersion;
|
||||
}
|
||||
|
||||
public void setBootVersion(String bootVersion) {
|
||||
this.bootVersion = bootVersion;
|
||||
}
|
||||
|
||||
public String getJavaVersion() {
|
||||
return javaVersion;
|
||||
}
|
||||
|
||||
public void setJavaVersion(String javaVersion) {
|
||||
this.javaVersion = javaVersion;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public String getPackaging() {
|
||||
return packaging;
|
||||
}
|
||||
|
||||
public void setPackaging(String packaging) {
|
||||
this.packaging = packaging;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
public void setErrorMessage(String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public boolean isInvalid() {
|
||||
return invalid;
|
||||
}
|
||||
|
||||
public void setInvalid(boolean invalid) {
|
||||
this.invalid = invalid;
|
||||
}
|
||||
|
||||
public boolean isInvalidJavaVersion() {
|
||||
return invalidJavaVersion;
|
||||
}
|
||||
|
||||
public void setInvalidJavaVersion(boolean invalidJavaVersion) {
|
||||
this.invalidJavaVersion = invalidJavaVersion;
|
||||
}
|
||||
|
||||
public boolean isInvalidLanguage() {
|
||||
return invalidLanguage;
|
||||
}
|
||||
|
||||
public void setInvalidLanguage(boolean invalidLanguage) {
|
||||
this.invalidLanguage = invalidLanguage;
|
||||
}
|
||||
|
||||
public boolean isInvalidPackaging() {
|
||||
return invalidPackaging;
|
||||
}
|
||||
|
||||
public void setInvalidPackaging(boolean invalidPackaging) {
|
||||
this.invalidPackaging = invalidPackaging;
|
||||
}
|
||||
|
||||
public boolean isInvalidType() {
|
||||
return invalidType;
|
||||
}
|
||||
|
||||
public void setInvalidType(boolean invalidType) {
|
||||
this.invalidType = invalidType;
|
||||
}
|
||||
|
||||
public List<String> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public List<String> getInvalidDependencies() {
|
||||
return invalidDependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProjectRequestDocument [generationTimestamp=" + generationTimestamp + ", "
|
||||
+ (requestIp != null ? "requestIp=" + requestIp + ", " : "")
|
||||
+ (requestIpv4 != null ? "requestIpv4=" + requestIpv4 + ", " : "")
|
||||
+ (requestCountry != null ? "requestCountry=" + requestCountry + ", "
|
||||
: "")
|
||||
+ (clientId != null ? "clientId=" + clientId + ", " : "")
|
||||
+ (clientVersion != null ? "clientVersion=" + clientVersion + ", " : "")
|
||||
+ (groupId != null ? "groupId=" + groupId + ", " : "")
|
||||
+ (artifactId != null ? "artifactId=" + artifactId + ", " : "")
|
||||
+ (packageName != null ? "packageName=" + packageName + ", " : "")
|
||||
+ (bootVersion != null ? "bootVersion=" + bootVersion + ", " : "")
|
||||
+ (javaVersion != null ? "javaVersion=" + javaVersion + ", " : "")
|
||||
+ (language != null ? "language=" + language + ", " : "")
|
||||
+ (packaging != null ? "packaging=" + packaging + ", " : "")
|
||||
+ (type != null ? "type=" + type + ", " : "")
|
||||
+ (dependencies != null ? "dependencies=" + dependencies + ", " : "")
|
||||
+ (errorMessage != null ? "errorMessage=" + errorMessage + ", " : "")
|
||||
+ "invalid=" + invalid + ", invalidJavaVersion=" + invalidJavaVersion
|
||||
+ ", invalidLanguage=" + invalidLanguage + ", invalidPackaging="
|
||||
+ invalidPackaging + ", invalidType=" + invalidType + ", "
|
||||
+ (invalidDependencies != null
|
||||
? "invalidDependencies=" + invalidDependencies : "")
|
||||
+ "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.stat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import io.spring.initializr.generator.ProjectFailedEvent;
|
||||
import io.spring.initializr.generator.ProjectRequest;
|
||||
import io.spring.initializr.generator.ProjectRequestEvent;
|
||||
import io.spring.initializr.metadata.InitializrMetadata;
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider;
|
||||
import io.spring.initializr.util.Agent;
|
||||
|
||||
/**
|
||||
* Create {@link ProjectRequestDocument} instances.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ProjectRequestDocumentFactory {
|
||||
|
||||
private static final Pattern IP_PATTERN = Pattern.compile("[0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*");
|
||||
|
||||
private final InitializrMetadataProvider metadataProvider;
|
||||
|
||||
public ProjectRequestDocumentFactory(InitializrMetadataProvider metadataProvider) {
|
||||
this.metadataProvider = metadataProvider;
|
||||
}
|
||||
|
||||
public ProjectRequestDocument createDocument(ProjectRequestEvent event) {
|
||||
InitializrMetadata metadata = metadataProvider.get();
|
||||
ProjectRequest request = event.getProjectRequest();
|
||||
|
||||
ProjectRequestDocument document = new ProjectRequestDocument();
|
||||
document.setGenerationTimestamp(event.getTimestamp());
|
||||
|
||||
handleCloudFlareHeaders(request, document);
|
||||
String candidate = (String) request.getParameters().get("x-forwarded-for");
|
||||
if (!StringUtils.hasText(document.getRequestIp()) && candidate!=null) {
|
||||
document.setRequestIp(candidate);
|
||||
document.setRequestIpv4(extractIpv4(candidate));
|
||||
}
|
||||
|
||||
Agent agent = extractAgentInformation(request);
|
||||
if (agent!=null) {
|
||||
document.setClientId(agent.getId().getId());
|
||||
document.setClientVersion(agent.getVersion());
|
||||
}
|
||||
|
||||
document.setGroupId(request.getGroupId());
|
||||
document.setArtifactId(request.getArtifactId());
|
||||
document.setPackageName(request.getPackageName());
|
||||
document.setBootVersion(request.getBootVersion());
|
||||
|
||||
document.setJavaVersion(request.getJavaVersion());
|
||||
if (StringUtils.hasText(request.getJavaVersion()) && metadata.getJavaVersions().get(request.getJavaVersion())==null) {
|
||||
document.setInvalid(true);
|
||||
document.setInvalidJavaVersion(true);
|
||||
}
|
||||
|
||||
document.setLanguage(request.getLanguage());
|
||||
if (StringUtils.hasText(request.getLanguage()) && metadata.getLanguages().get(request.getLanguage())==null) {
|
||||
document.setInvalid(true);
|
||||
document.setInvalidLanguage(true);
|
||||
}
|
||||
|
||||
document.setPackaging(request.getPackaging());
|
||||
if (StringUtils.hasText(request.getPackaging()) && metadata.getPackagings().get(request.getPackaging())==null) {
|
||||
document.setInvalid(true);
|
||||
document.setInvalidPackaging(true);
|
||||
}
|
||||
|
||||
document.setType(request.getType());
|
||||
if (StringUtils.hasText(request.getType()) && metadata.getTypes().get(request.getType())==null) {
|
||||
document.setInvalid(true);
|
||||
document.setInvalidType(true);
|
||||
}
|
||||
|
||||
// Let's not rely on the resolved dependencies here
|
||||
List<String> dependencies = new ArrayList<>();
|
||||
dependencies.addAll(request.getStyle());
|
||||
dependencies.addAll(request.getDependencies());
|
||||
dependencies.forEach( id -> {
|
||||
if (metadata.getDependencies().get(id)!=null) {
|
||||
document.getDependencies().add(id);
|
||||
} else {
|
||||
document.setInvalid(true);
|
||||
document.getInvalidDependencies().add(id);
|
||||
}
|
||||
});
|
||||
|
||||
// Let's make sure that the document is flagged as invalid no matter what
|
||||
if (event instanceof ProjectFailedEvent) {
|
||||
ProjectFailedEvent failed = (ProjectFailedEvent) event;
|
||||
document.setInvalid(true);
|
||||
if (failed.getCause()!=null) {
|
||||
document.setErrorMessage(failed.getCause().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
private static void handleCloudFlareHeaders(ProjectRequest request, ProjectRequestDocument document) {
|
||||
String candidate = (String) request.getParameters().get("cf-connecting-ip");
|
||||
if (StringUtils.hasText(candidate)) {
|
||||
document.setRequestIp(candidate);
|
||||
document.setRequestIpv4(extractIpv4(candidate));
|
||||
}
|
||||
String country = (String) request.getParameters().get("cf-ipcountry");
|
||||
if (StringUtils.hasText(country) && !country.toLowerCase().equals("xx")) {
|
||||
document.setRequestCountry(country);
|
||||
}
|
||||
}
|
||||
|
||||
private static Agent extractAgentInformation(ProjectRequest request) {
|
||||
String userAgent = (String) request.getParameters().get("user-agent");
|
||||
if (StringUtils.hasText(userAgent)) {
|
||||
return Agent.fromUserAgent(userAgent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String extractIpv4(String candidate) {
|
||||
if (StringUtils.hasText(candidate)) {
|
||||
Matcher matcher = IP_PATTERN.matcher(candidate);
|
||||
if (matcher.find()) {
|
||||
return matcher.group();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.actuate.stat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Statistics-related properties.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ConfigurationProperties("initializr.stats")
|
||||
public class StatsProperties {
|
||||
|
||||
private final Elastic elastic = new Elastic();
|
||||
|
||||
public Elastic getElastic() {
|
||||
return elastic;
|
||||
}
|
||||
|
||||
public static final class Elastic {
|
||||
|
||||
/**
|
||||
* Elastic service uri.
|
||||
*/
|
||||
private String uri;
|
||||
|
||||
/**
|
||||
* Elastic service username.
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* Elastic service password
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* Name of the index.
|
||||
*/
|
||||
private String indexName = "initializr";
|
||||
|
||||
/**
|
||||
* Name of the entity to use to publish stats.
|
||||
*/
|
||||
private String entityName = "request";
|
||||
|
||||
/**
|
||||
* Number of attempts before giving up.
|
||||
*/
|
||||
private int maxAttempts = 3;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getIndexName() {
|
||||
return indexName;
|
||||
}
|
||||
|
||||
public void setIndexName(String indexName) {
|
||||
this.indexName = indexName;
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
public void setEntityName(String entityName) {
|
||||
this.entityName = entityName;
|
||||
}
|
||||
|
||||
public int getMaxAttempts() {
|
||||
return maxAttempts;
|
||||
}
|
||||
|
||||
public void setMaxAttempts(int maxAttempts) {
|
||||
this.maxAttempts = maxAttempts;
|
||||
}
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = cleanUri(uri);
|
||||
}
|
||||
|
||||
public URI getEntityUrl() {
|
||||
String string = uri + "/" + indexName + "/" + entityName;
|
||||
try {
|
||||
return new URI(string);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
throw new IllegalStateException("Cannot create entity URL: " + string, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String cleanUri(String contextPath) {
|
||||
if (StringUtils.hasText(contextPath) && contextPath.endsWith("/")) {
|
||||
return contextPath.substring(0, contextPath.length() - 1);
|
||||
}
|
||||
return contextPath;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests
|
||||
import org.junit.Test
|
||||
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertTrue
|
||||
|
||||
/**
|
||||
* Tests for actuator specific features.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ActiveProfiles('test-default')
|
||||
class ActuatorIntegrationTests extends AbstractInitializrControllerIntegrationTests {
|
||||
|
||||
private final def slurper = new JsonSlurper()
|
||||
|
||||
@Test
|
||||
void infoHasExternalProperties() {
|
||||
def body = restTemplate.getForObject(createUrl('/info'), String)
|
||||
assertTrue("Wrong body:\n$body", body.contains('"spring-boot"'))
|
||||
assertTrue("Wrong body:\n$body", body.contains('"version":"1.1.4.RELEASE"'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void metricsAvailableByDefault() {
|
||||
downloadZip('/starter.zip?packaging=jar&javaVersion=1.8&style=web&style=jpa')
|
||||
def result = metricsEndpoint()
|
||||
def requests = result['counter.initializr.requests']
|
||||
def packaging = result['counter.initializr.packaging.jar']
|
||||
def javaVersion = result['counter.initializr.java_version.1_8']
|
||||
def webDependency = result['counter.initializr.dependency.web']
|
||||
def jpaDependency = result['counter.initializr.dependency.jpa']
|
||||
|
||||
downloadZip('/starter.zip?packaging=jar&javaVersion=1.8&style=web') // No jpa dep this time
|
||||
|
||||
def updatedResult = metricsEndpoint()
|
||||
assertEquals 'Number of request should have increased',
|
||||
requests + 1, updatedResult['counter.initializr.requests']
|
||||
assertEquals 'jar packaging metric should have increased',
|
||||
packaging + 1, updatedResult['counter.initializr.packaging.jar']
|
||||
assertEquals 'java version metric should have increased',
|
||||
javaVersion + 1, updatedResult['counter.initializr.java_version.1_8']
|
||||
assertEquals 'web dependency metric should have increased',
|
||||
webDependency + 1, updatedResult['counter.initializr.dependency.web']
|
||||
assertEquals 'jpa dependency metric should not have increased',
|
||||
jpaDependency, updatedResult['counter.initializr.dependency.jpa']
|
||||
}
|
||||
|
||||
private def metricsEndpoint() {
|
||||
parseJson(restTemplate.getForObject(createUrl('/metrics'), String))
|
||||
}
|
||||
|
||||
private def parseJson(String content) {
|
||||
slurper.parseText(content)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.info
|
||||
|
||||
import io.spring.initializr.metadata.BillOfMaterials
|
||||
import io.spring.initializr.metadata.InitializrMetadata
|
||||
import io.spring.initializr.metadata.SimpleInitializrMetadataProvider
|
||||
import io.spring.initializr.test.metadata.InitializrMetadataTestBuilder
|
||||
import org.junit.Test
|
||||
|
||||
import org.springframework.boot.actuate.info.Info
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat
|
||||
import static org.assertj.core.api.Assertions.entry
|
||||
|
||||
/**
|
||||
* Tests for {@link BomRangesInfoContributor}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class BomRangesInfoContributorTests {
|
||||
|
||||
@Test
|
||||
void noBom() {
|
||||
def metadata = InitializrMetadataTestBuilder.withDefaults().build()
|
||||
def info = getInfo(metadata)
|
||||
assertThat(info.details).doesNotContainKeys('bom-ranges')
|
||||
}
|
||||
|
||||
@Test
|
||||
void noMapping() {
|
||||
def bom = new BillOfMaterials(groupId: 'com.example', artifactId: 'bom', version: '1.0.0')
|
||||
def metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addBom('foo', bom)
|
||||
.build()
|
||||
def info = getInfo(metadata)
|
||||
assertThat(info.details).doesNotContainKeys('bom-ranges')
|
||||
}
|
||||
|
||||
@Test
|
||||
void withMappings() {
|
||||
BillOfMaterials bom = new BillOfMaterials(groupId: 'com.example',
|
||||
artifactId: 'bom', version: '1.0.0')
|
||||
bom.mappings << new BillOfMaterials.Mapping(
|
||||
versionRange: '[1.3.0.RELEASE,1.3.8.RELEASE]', version: '1.1.0')
|
||||
bom.mappings << new BillOfMaterials.Mapping(
|
||||
versionRange: '1.3.8.BUILD-SNAPSHOT', version: '1.1.1-SNAPSHOT')
|
||||
def metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addBom('foo', bom)
|
||||
.build()
|
||||
def info = getInfo(metadata)
|
||||
assertThat(info.details).containsKeys('bom-ranges')
|
||||
Map<String,Object> ranges = info.details['bom-ranges'] as Map<String, Object>
|
||||
assertThat(ranges).containsOnlyKeys('foo')
|
||||
Map<String,Object> foo = ranges['foo'] as Map<String, Object>
|
||||
assertThat(foo).containsExactly(
|
||||
entry('1.1.0', 'Spring Boot >=1.3.0.RELEASE and <=1.3.8.RELEASE'),
|
||||
entry('1.1.1-SNAPSHOT', 'Spring Boot >=1.3.8.BUILD-SNAPSHOT'))
|
||||
}
|
||||
|
||||
private static Info getInfo(InitializrMetadata metadata) {
|
||||
Info.Builder builder = new Info.Builder()
|
||||
new BomRangesInfoContributor(new SimpleInitializrMetadataProvider(metadata))
|
||||
.contribute(builder)
|
||||
builder.build()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.metric
|
||||
|
||||
import io.spring.initializr.actuate.test.RedisRunning
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent
|
||||
import io.spring.initializr.generator.ProjectRequest
|
||||
import io.spring.initializr.metadata.InitializrMetadataBuilder
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider
|
||||
import io.spring.initializr.metadata.InitializrProperties
|
||||
import io.spring.initializr.metadata.SimpleInitializrMetadataProvider
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.test.context.junit4.SpringRunner
|
||||
|
||||
import static org.junit.Assert.assertTrue
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringRunner)
|
||||
@SpringBootTest(classes = Config, properties = ['spring.metrics.export.delayMillis:500',
|
||||
'spring.metrics.export.enabled:true',
|
||||
'initializr.metrics.prefix:test.prefix', 'initializr.metrics.key:key.test'])
|
||||
class MetricsExportTests {
|
||||
|
||||
@Rule
|
||||
public RedisRunning running = new RedisRunning()
|
||||
|
||||
@Autowired
|
||||
ProjectGenerationMetricsListener listener
|
||||
|
||||
@Autowired
|
||||
@Qualifier("writer")
|
||||
MetricWriter writer
|
||||
|
||||
RedisMetricRepository repository
|
||||
|
||||
@Before
|
||||
void init() {
|
||||
repository = (RedisMetricRepository) writer
|
||||
repository.findAll().each {
|
||||
repository.reset(it.name)
|
||||
}
|
||||
assertTrue("Metrics not empty", repository.findAll().size() == 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
void exportAndCheckMetricsExist() {
|
||||
listener.onGeneratedProject(new ProjectGeneratedEvent(new ProjectRequest()))
|
||||
Thread.sleep(1000L)
|
||||
assertTrue("No metrics exported", repository.findAll().size() > 0)
|
||||
}
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@EnableConfigurationProperties(InitializrProperties)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
InitializrMetadataProvider initializrMetadataProvider(InitializrProperties properties) {
|
||||
def metadata = InitializrMetadataBuilder.fromInitializrProperties(properties).build()
|
||||
new SimpleInitializrMetadataProvider(metadata)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.metric
|
||||
|
||||
import io.spring.initializr.actuate.test.MetricsAssert
|
||||
import io.spring.initializr.actuate.test.TestCounterService
|
||||
import io.spring.initializr.generator.ProjectFailedEvent
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent
|
||||
import io.spring.initializr.generator.ProjectRequest
|
||||
import io.spring.initializr.metadata.Dependency
|
||||
import io.spring.initializr.metadata.InitializrMetadata
|
||||
import io.spring.initializr.test.metadata.InitializrMetadataTestBuilder
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ProjectGenerationMetricsListenerTests {
|
||||
|
||||
private InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addDependencyGroup('core', 'web', 'security', 'spring-data').build()
|
||||
|
||||
private ProjectGenerationMetricsListener listener
|
||||
private MetricsAssert metricsAssert
|
||||
|
||||
|
||||
@Before
|
||||
void setup() {
|
||||
def counterService = new TestCounterService()
|
||||
listener = new ProjectGenerationMetricsListener(counterService)
|
||||
metricsAssert = new MetricsAssert(counterService)
|
||||
}
|
||||
|
||||
@Test
|
||||
void projectGenerationCount() {
|
||||
def request = initialize()
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.requests')
|
||||
}
|
||||
|
||||
@Test
|
||||
void projectGenerationCountWithFailure() {
|
||||
def request = initialize()
|
||||
request.resolve(metadata)
|
||||
fireProjectFailedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.requests')
|
||||
metricsAssert.hasValue(1, 'initializr.failures')
|
||||
}
|
||||
|
||||
@Test
|
||||
void dependencies() {
|
||||
def request = initialize()
|
||||
request.style << 'security' << 'spring-data'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.dependency.security',
|
||||
'initializr.dependency.spring-data')
|
||||
}
|
||||
|
||||
@Test
|
||||
void noDependencies() {
|
||||
def request = initialize()
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasNoValue('initializr.dependency.')
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvedWebDependency() {
|
||||
def request = initialize()
|
||||
request.style << 'spring-data'
|
||||
request.packaging = 'war'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.dependency.web',
|
||||
'initializr.dependency.spring-data')
|
||||
}
|
||||
|
||||
@Test
|
||||
void aliasedDependencyUseStandardId() {
|
||||
def dependency = new Dependency()
|
||||
dependency.id = 'foo'
|
||||
dependency.aliases << 'foo-old'
|
||||
def metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addDependencyGroup('core', dependency).build()
|
||||
def request = new ProjectRequest()
|
||||
request.initialize(metadata)
|
||||
request.style << 'foo-old'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.dependency.foo') // standard id is used
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultType() {
|
||||
def request = initialize()
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.type.maven-project')
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitType() {
|
||||
def request = initialize()
|
||||
request.type = 'gradle-build'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.type.gradle-build')
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultPackaging() {
|
||||
def request = initialize()
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.packaging.jar')
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitPackaging() {
|
||||
def request = initialize()
|
||||
request.packaging = 'war'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.packaging.war')
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultJavaVersion() {
|
||||
def request = initialize()
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.java_version.1_8')
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitJavaVersion() {
|
||||
def request = initialize()
|
||||
request.javaVersion = '1.7'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.java_version.1_7')
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultLanguage() {
|
||||
def request = initialize()
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.language.java')
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitGroovyLanguage() {
|
||||
def request = initialize()
|
||||
request.language = 'groovy'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.language.groovy')
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitKotlinLanguage() {
|
||||
def request = initialize()
|
||||
request.language = 'kotlin'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.language.kotlin')
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultBootVersion() {
|
||||
def request = initialize()
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.boot_version.1_2_3_RELEASE')
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitBootVersion() {
|
||||
def request = initialize()
|
||||
request.bootVersion = '1.0.2.RELEASE'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.boot_version.1_0_2_RELEASE')
|
||||
}
|
||||
|
||||
@Test
|
||||
void userAgentAvailable() {
|
||||
def request = initialize()
|
||||
request.parameters['user-agent'] = 'HTTPie/0.9.2'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.client_id.httpie')
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectAllMetrics() {
|
||||
def request = initialize()
|
||||
request.style << 'web' << 'security'
|
||||
request.type = 'gradle-project'
|
||||
request.packaging = 'jar'
|
||||
request.javaVersion = '1.6'
|
||||
request.language = 'groovy'
|
||||
request.bootVersion = '1.0.2.RELEASE'
|
||||
request.parameters['user-agent'] = 'SpringBootCli/1.3.0.RELEASE'
|
||||
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.requests',
|
||||
'initializr.dependency.web', 'initializr.dependency.security',
|
||||
'initializr.type.gradle-project', 'initializr.packaging.jar',
|
||||
'initializr.java_version.1_6', 'initializr.language.groovy',
|
||||
'initializr.boot_version.1_0_2_RELEASE',
|
||||
'initializr.client_id.spring').metricsCount(9)
|
||||
}
|
||||
|
||||
@Test
|
||||
void incrementMetrics() {
|
||||
def request = initialize()
|
||||
request.style << 'security' << 'spring-data'
|
||||
request.resolve(metadata)
|
||||
fireProjectGeneratedEvent(request)
|
||||
metricsAssert.hasValue(1, 'initializr.requests',
|
||||
'initializr.dependency.security', 'initializr.dependency.spring-data')
|
||||
|
||||
def anotherRequest = initialize()
|
||||
anotherRequest.style << 'web' << 'spring-data'
|
||||
anotherRequest.resolve(metadata)
|
||||
fireProjectGeneratedEvent(anotherRequest)
|
||||
metricsAssert.hasValue(2, 'initializr.dependency.spring-data',
|
||||
'initializr.dependency.spring-data')
|
||||
metricsAssert.hasValue(1, 'initializr.dependency.web',
|
||||
'initializr.dependency.security')
|
||||
}
|
||||
|
||||
private fireProjectGeneratedEvent(ProjectRequest projectRequest) {
|
||||
listener.onGeneratedProject(new ProjectGeneratedEvent(projectRequest))
|
||||
}
|
||||
|
||||
private fireProjectFailedEvent(ProjectRequest projectRequest) {
|
||||
listener.onFailedProject(new ProjectFailedEvent(projectRequest, null))
|
||||
}
|
||||
|
||||
private ProjectRequest initialize() {
|
||||
def request = new ProjectRequest()
|
||||
request.initialize(metadata)
|
||||
request
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.stat
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import io.spring.initializr.web.AbstractFullStackInitializrIntegrationTests
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Import
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.RequestEntity
|
||||
import org.springframework.test.context.ActiveProfiles
|
||||
import org.springframework.util.Base64Utils
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestMethod
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.springframework.web.client.HttpClientErrorException
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertFalse
|
||||
import static org.junit.Assert.assertNotNull
|
||||
import static org.junit.Assert.assertTrue
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
/**
|
||||
* Integration tests for stats processing.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Import(StatsMockController)
|
||||
@ActiveProfiles(['test-default', 'test-custom-stats'])
|
||||
class MainControllerStatsIntegrationTests
|
||||
extends AbstractFullStackInitializrIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private StatsMockController statsMockController
|
||||
|
||||
@Autowired
|
||||
private StatsProperties statsProperties
|
||||
|
||||
private final JsonSlurper slurper = new JsonSlurper()
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.statsMockController.stats.clear()
|
||||
// Make sure our mock is going to be invoked with the stats
|
||||
this.statsProperties.elastic.uri = "http://localhost:$port/elastic"
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleProject() {
|
||||
downloadArchive('/starter.zip?groupId=com.foo&artifactId=bar&dependencies=web')
|
||||
assertEquals 'No stat got generated', 1, statsMockController.stats.size()
|
||||
def content = statsMockController.stats[0]
|
||||
|
||||
def json = slurper.parseText(content.json)
|
||||
assertEquals 'com.foo', json.groupId
|
||||
assertEquals 'bar', json.artifactId
|
||||
assertEquals 1, json.dependencies.size()
|
||||
assertEquals 'web', json.dependencies[0]
|
||||
}
|
||||
|
||||
@Test
|
||||
void authorizationHeaderIsSet() {
|
||||
downloadArchive('/starter.zip')
|
||||
assertEquals 'No stat got generated', 1, statsMockController.stats.size()
|
||||
def content = statsMockController.stats[0]
|
||||
|
||||
def authorization = content.authorization
|
||||
assertNotNull 'Authorization header must be set', authorization
|
||||
assertTrue 'Wrong value for authorization header', authorization.startsWith('Basic ')
|
||||
def token = authorization.substring('Basic '.length(), authorization.size())
|
||||
def data = new String(Base64Utils.decodeFromString(token)).split(':')
|
||||
assertEquals "Wrong user from $token", 'test-user', data[0]
|
||||
assertEquals "Wrong password $token", 'test-password', data[1]
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestIpNotSetByDefault() {
|
||||
downloadArchive('/starter.zip?groupId=com.foo&artifactId=bar&dependencies=web')
|
||||
assertEquals 'No stat got generated', 1, statsMockController.stats.size()
|
||||
def content = statsMockController.stats[0]
|
||||
|
||||
def json = slurper.parseText(content.json)
|
||||
assertFalse 'requestIp property should not be set', json.containsKey('requestIp')
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestIpIsSetWhenHeaderIsPresent() {
|
||||
RequestEntity<?> request = RequestEntity.get(new URI(createUrl('/starter.zip')))
|
||||
.header('X-FORWARDED-FOR', '10.0.0.123').build()
|
||||
restTemplate.exchange(request, String)
|
||||
assertEquals 'No stat got generated', 1, statsMockController.stats.size()
|
||||
def content = statsMockController.stats[0]
|
||||
|
||||
def json = slurper.parseText(content.json)
|
||||
assertEquals 'Wrong requestIp', '10.0.0.123', json.requestIp
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestIpv4IsNotSetWhenHeaderHasGarbage() {
|
||||
RequestEntity<?> request = RequestEntity.get(new URI(createUrl('/starter.zip')))
|
||||
.header('x-forwarded-for', 'foo-bar').build()
|
||||
restTemplate.exchange(request, String)
|
||||
assertEquals 'No stat got generated', 1, statsMockController.stats.size()
|
||||
def content = statsMockController.stats[0]
|
||||
|
||||
def json = slurper.parseText(content.json)
|
||||
assertFalse 'requestIpv4 property should not be set if value is not a valid IPv4',
|
||||
json.containsKey('requestIpv4')
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestCountryIsNotSetWhenHeaderIsSetToXX() {
|
||||
RequestEntity<?> request = RequestEntity.get(new URI(createUrl('/starter.zip')))
|
||||
.header('cf-ipcountry', 'XX').build()
|
||||
restTemplate.exchange(request, String)
|
||||
assertEquals 'No stat got generated', 1, statsMockController.stats.size()
|
||||
def content = statsMockController.stats[0]
|
||||
|
||||
def json = slurper.parseText(content.json)
|
||||
assertFalse 'requestCountry property should not be set if value is set to xx',
|
||||
json.containsKey('requestCountry')
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidProjectSillHasStats() {
|
||||
try {
|
||||
downloadArchive('/starter.zip?type=invalid-type')
|
||||
fail("Should have failed to generate project with invalid type")
|
||||
} catch (HttpClientErrorException ex) {
|
||||
assertEquals HttpStatus.BAD_REQUEST, ex.statusCode
|
||||
}
|
||||
assertEquals 'No stat got generated', 1, statsMockController.stats.size()
|
||||
def content = statsMockController.stats[0]
|
||||
|
||||
def json = slurper.parseText(content.json)
|
||||
assertEquals 'com.example', json.groupId
|
||||
assertEquals 'demo', json.artifactId
|
||||
assertEquals true, json.invalid
|
||||
assertEquals true, json.invalidType
|
||||
assertNotNull json.errorMessage
|
||||
assertTrue json.errorMessage.contains('invalid-type')
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorPublishingStatsDoesNotBubbleUp() {
|
||||
this.statsProperties.elastic.uri = "http://localhost:$port/elastic-error"
|
||||
downloadArchive('/starter.zip')
|
||||
assertEquals 'No stat should be available', 0, statsMockController.stats.size()
|
||||
}
|
||||
|
||||
|
||||
@RestController
|
||||
static class StatsMockController {
|
||||
|
||||
private final List<Content> stats = []
|
||||
|
||||
@RequestMapping(path = '/elastic/test/my-entity', method = RequestMethod.POST)
|
||||
void handleProjectRequestDocument(RequestEntity<String> input) {
|
||||
def authorization = input.headers.getFirst(HttpHeaders.AUTHORIZATION)
|
||||
def content = new Content(authorization: authorization, json: input.body)
|
||||
this.stats << content
|
||||
}
|
||||
|
||||
@RequestMapping(path = '/elastic-error/test/my-entity', method = RequestMethod.POST)
|
||||
void handleExpectedError() {
|
||||
throw new IllegalStateException('Expected exception')
|
||||
}
|
||||
|
||||
static class Content {
|
||||
|
||||
String authorization
|
||||
|
||||
String json
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.stat
|
||||
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent
|
||||
import io.spring.initializr.generator.ProjectRequest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy
|
||||
import org.springframework.retry.support.RetryTemplate
|
||||
import org.springframework.test.web.client.MockRestServiceServer
|
||||
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ProjectGenerationStatPublisherTests extends AbstractInitializrStatTests {
|
||||
|
||||
private StatsProperties properties
|
||||
private RetryTemplate retryTemplate
|
||||
private ProjectGenerationStatPublisher statPublisher
|
||||
private MockRestServiceServer mockServer
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.properties = createProperties()
|
||||
ProjectRequestDocumentFactory documentFactory =
|
||||
new ProjectRequestDocumentFactory(createProvider(metadata))
|
||||
this.retryTemplate = new RetryTemplate()
|
||||
this.statPublisher = new ProjectGenerationStatPublisher(documentFactory, properties, retryTemplate)
|
||||
mockServer = MockRestServiceServer.createServer(this.statPublisher.restTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publishSimpleDocument() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.groupId = 'com.example.foo'
|
||||
request.artifactId = 'my-project'
|
||||
|
||||
mockServer.expect(requestTo('http://example.com/elastic/initializr/request'))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andExpect(jsonPath('$.groupId').value('com.example.foo'))
|
||||
.andExpect(jsonPath('$.artifactId').value('my-project'))
|
||||
.andRespond(withStatus(HttpStatus.CREATED)
|
||||
.body(mockResponse(UUID.randomUUID().toString(), true))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
)
|
||||
|
||||
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request))
|
||||
mockServer.verify()
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recoverFromError() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
|
||||
mockServer.expect(requestTo('http://example.com/elastic/initializr/request'))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR))
|
||||
|
||||
mockServer.expect(requestTo('http://example.com/elastic/initializr/request'))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR))
|
||||
|
||||
mockServer.expect(requestTo('http://example.com/elastic/initializr/request'))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.CREATED)
|
||||
.body(mockResponse(UUID.randomUUID().toString(), true))
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
|
||||
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request))
|
||||
mockServer.verify()
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fatalErrorOnlyLogs() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
this.retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2,
|
||||
Collections.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)))
|
||||
|
||||
mockServer.expect(requestTo('http://example.com/elastic/initializr/request'))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR))
|
||||
|
||||
mockServer.expect(requestTo('http://example.com/elastic/initializr/request'))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR))
|
||||
|
||||
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request))
|
||||
mockServer.verify()
|
||||
}
|
||||
|
||||
private static String mockResponse(String id, boolean created) {
|
||||
'{"_index":"initializr","_type":"request","_id":"' + id + '","_version":1,"_shards"' +
|
||||
':{"total":1,"successful":1,"failed":0},"created":' + created + '}'
|
||||
}
|
||||
|
||||
private static StatsProperties createProperties() {
|
||||
def properties = new StatsProperties()
|
||||
properties.elastic.uri = 'http://example.com/elastic'
|
||||
properties.elastic.username = 'foo'
|
||||
properties.elastic.password = 'bar'
|
||||
properties
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.stat
|
||||
|
||||
import io.spring.initializr.generator.ProjectFailedEvent
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent
|
||||
import io.spring.initializr.generator.ProjectRequest
|
||||
import org.junit.Test
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.assertFalse
|
||||
import static org.junit.Assert.assertNull
|
||||
import static org.junit.Assert.assertTrue
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
|
||||
|
||||
private final ProjectRequestDocumentFactory factory =
|
||||
new ProjectRequestDocumentFactory(createProvider(metadata))
|
||||
|
||||
@Test
|
||||
void createDocumentForSimpleProject() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals event.timestamp, document.generationTimestamp
|
||||
assertEquals null, document.requestIp
|
||||
assertEquals 'com.example', document.groupId
|
||||
assertEquals 'demo', document.artifactId
|
||||
assertEquals 'com.example', document.packageName
|
||||
assertEquals '1.2.3.RELEASE', document.bootVersion
|
||||
assertEquals '1.8', document.javaVersion
|
||||
assertEquals 'java', document.language
|
||||
assertEquals 'jar', document.packaging
|
||||
assertEquals 'maven-project', document.type
|
||||
assertEquals 0, document.dependencies.size()
|
||||
assertValid document
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentWithRequestIp() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.parameters['x-forwarded-for'] = '10.0.0.123'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals '10.0.0.123', document.requestIp
|
||||
assertEquals '10.0.0.123', document.requestIpv4
|
||||
assertNull document.requestCountry
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentWithRequestIpv6() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.parameters['x-forwarded-for'] = '2001:db8:a0b:12f0::1'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals '2001:db8:a0b:12f0::1', document.requestIp
|
||||
assertNull document.requestIpv4
|
||||
assertNull document.requestCountry
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentWithCloudFlareHeaders() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.parameters['cf-connecting-ip'] = '10.0.0.123'
|
||||
request.parameters['cf-ipcountry'] = 'BE'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals '10.0.0.123', document.requestIp
|
||||
assertEquals '10.0.0.123', document.requestIpv4
|
||||
assertEquals 'BE', document.requestCountry
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentWithCloudFlareIpv6() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.parameters['cf-connecting-ip'] = '2001:db8:a0b:12f0::1'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals '2001:db8:a0b:12f0::1', document.requestIp
|
||||
assertNull document.requestIpv4
|
||||
assertNull document.requestCountry
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentWithCloudFlareHeadersAndOtherHeaders() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.parameters['cf-connecting-ip'] = '10.0.0.123'
|
||||
request.parameters['x-forwarded-for'] = '192.168.1.101'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals '10.0.0.123', document.requestIp
|
||||
assertEquals '10.0.0.123', document.requestIpv4
|
||||
assertNull document.requestCountry
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentWithCloudFlareCountrySetToXX() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.parameters['cf-connecting-ip'] = 'Xx' // case insensitive
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertNull document.requestCountry
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentWithUserAgent() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.parameters['user-agent'] = 'HTTPie/0.8.0'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals 'httpie', document.clientId
|
||||
assertEquals '0.8.0', document.clientVersion
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentWithUserAgentNoVersion() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.parameters['user-agent'] = 'IntelliJ IDEA'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals 'intellijidea', document.clientId
|
||||
assertEquals null, document.clientVersion
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentInvalidJavaVersion() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.javaVersion = '1.2'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals '1.2', document.javaVersion
|
||||
assertTrue document.invalid
|
||||
assertTrue document.invalidJavaVersion
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentInvalidLanguage() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.language = 'c++'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals 'c++', document.language
|
||||
assertTrue document.invalid
|
||||
assertTrue document.invalidLanguage
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentInvalidPackaging() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.packaging = 'ear'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals 'ear', document.packaging
|
||||
assertTrue document.invalid
|
||||
assertTrue document.invalidPackaging
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentInvalidType() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.type = 'ant-project'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals 'ant-project', document.type
|
||||
assertTrue document.invalid
|
||||
assertTrue document.invalidType
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentInvalidDependency() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
request.dependencies << 'web' << 'invalid' << 'data-jpa' << 'invalid-2'
|
||||
def event = new ProjectGeneratedEvent(request)
|
||||
def document = factory.createDocument(event)
|
||||
assertEquals 'web', document.dependencies[0]
|
||||
assertEquals 'data-jpa', document.dependencies[1]
|
||||
assertEquals 2, document.dependencies.size()
|
||||
assertTrue document.invalid
|
||||
assertEquals 'invalid', document.invalidDependencies[0]
|
||||
assertEquals 'invalid-2', document.invalidDependencies[1]
|
||||
assertEquals 2, document.invalidDependencies.size()
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDocumentWithProjectFailedEvent() {
|
||||
ProjectRequest request = createProjectRequest()
|
||||
def event = new ProjectFailedEvent(request, new IllegalStateException('my test message'))
|
||||
def document = factory.createDocument(event)
|
||||
assertTrue document.invalid
|
||||
assertEquals 'my test message', document.errorMessage
|
||||
}
|
||||
|
||||
private static void assertValid(ProjectRequestDocument document) {
|
||||
assertFalse document.invalid
|
||||
assertFalse document.invalidJavaVersion
|
||||
assertFalse document.invalidLanguage
|
||||
assertFalse document.invalidPackaging
|
||||
assertEquals 0, document.invalidDependencies.size()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.test
|
||||
|
||||
import static org.junit.Assert.assertEquals
|
||||
import static org.junit.Assert.fail
|
||||
|
||||
/**
|
||||
* Metrics assertion based on {@link TestCounterService}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class MetricsAssert {
|
||||
|
||||
private final TestCounterService counterService
|
||||
|
||||
MetricsAssert(TestCounterService counterService) {
|
||||
this.counterService = counterService
|
||||
}
|
||||
|
||||
MetricsAssert hasValue(long value, String... metrics) {
|
||||
metrics.each {
|
||||
def actual = counterService.values[it]
|
||||
if (actual == null) {
|
||||
fail("Metric '$it' not found, got '${counterService.values.keySet()}")
|
||||
}
|
||||
assertEquals "Wrong value for metric $it", value, actual
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
MetricsAssert hasNoValue(String... metrics) {
|
||||
metrics.each {
|
||||
assertEquals "Metric '$it' should not be registered", null, counterService.values[it]
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
MetricsAssert metricsCount(int count) {
|
||||
assertEquals "Wrong number of metrics, got '${counterService.values.keySet()}",
|
||||
count, counterService.values.size()
|
||||
this
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import groovy.json.JsonSlurper;
|
||||
import io.spring.initializr.web.AbstractInitializrControllerIntegrationTests;
|
||||
|
||||
/**
|
||||
* Tests for actuator specific features.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ActiveProfiles("test-default")
|
||||
public class ActuatorIntegrationTests
|
||||
extends AbstractInitializrControllerIntegrationTests {
|
||||
|
||||
private final JsonSlurper slurper = new JsonSlurper();
|
||||
|
||||
@Test
|
||||
public void infoHasExternalProperties() {
|
||||
String body = getRestTemplate().getForObject(createUrl("/info"), String.class);
|
||||
assertTrue("Wrong body:\n" + body, body.contains("\"spring-boot\""));
|
||||
assertTrue("Wrong body:\n" + body,
|
||||
body.contains("\"version\":\"1.1.4.RELEASE\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metricsAvailableByDefault() {
|
||||
downloadZip("/starter.zip?packaging=jar&javaVersion=1.8&style=web&style=jpa");
|
||||
Map<String, Integer> result = metricsEndpoint();
|
||||
Integer requests = result.get("counter.initializr.requests");
|
||||
Integer packaging = result.get("counter.initializr.packaging.jar");
|
||||
Integer javaVersion = result.get("counter.initializr.java_version.1_8");
|
||||
Integer webDependency = result.get("counter.initializr.dependency.web");
|
||||
Integer jpaDependency = result.get("counter.initializr.dependency.jpa");
|
||||
|
||||
// No jpa dep this time
|
||||
downloadZip("/starter.zip?packaging=jar&javaVersion=1.8&style=web");
|
||||
|
||||
Map<String, Integer> updatedResult = metricsEndpoint();
|
||||
assertEquals("Number of request should have increased", requests + 1,
|
||||
updatedResult.get("counter.initializr.requests").intValue());
|
||||
assertEquals("jar packaging metric should have increased", packaging + 1,
|
||||
updatedResult.get("counter.initializr.packaging.jar").intValue());
|
||||
assertEquals("java version metric should have increased", javaVersion + 1,
|
||||
updatedResult.get("counter.initializr.java_version.1_8").intValue());
|
||||
assertEquals("web dependency metric should have increased", webDependency + 1,
|
||||
updatedResult.get("counter.initializr.dependency.web").intValue());
|
||||
assertEquals("jpa dependency metric should not have increased", jpaDependency,
|
||||
updatedResult.get("counter.initializr.dependency.jpa"));
|
||||
}
|
||||
|
||||
private Map<String, Integer> metricsEndpoint() {
|
||||
return parseJson(getRestTemplate().getForObject(createUrl("/metrics"), String.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Integer> parseJson(String content) {
|
||||
return (Map<String, Integer>) slurper.parseText(content);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.info;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.actuate.info.Info;
|
||||
|
||||
import io.spring.initializr.metadata.BillOfMaterials;
|
||||
import io.spring.initializr.metadata.InitializrMetadata;
|
||||
import io.spring.initializr.metadata.SimpleInitializrMetadataProvider;
|
||||
import io.spring.initializr.test.metadata.InitializrMetadataTestBuilder;
|
||||
|
||||
/**
|
||||
* Tests for {@link BomRangesInfoContributor}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class BomRangesInfoContributorTests {
|
||||
|
||||
@Test
|
||||
public void noBom() {
|
||||
InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.build();
|
||||
Info info = getInfo(metadata);
|
||||
assertThat(info.getDetails()).doesNotContainKeys("bom-ranges");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noMapping() {
|
||||
BillOfMaterials bom = BillOfMaterials.create("com.example", "bom", "1.0.0");
|
||||
InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addBom("foo", bom).build();
|
||||
Info info = getInfo(metadata);
|
||||
assertThat(info.getDetails()).doesNotContainKeys("bom-ranges");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withMappings() {
|
||||
BillOfMaterials bom = BillOfMaterials.create("com.example", "bom", "1.0.0");
|
||||
bom.getMappings().add(
|
||||
BillOfMaterials.Mapping.create("[1.3.0.RELEASE,1.3.8.RELEASE]", "1.1.0"));
|
||||
bom.getMappings().add(
|
||||
BillOfMaterials.Mapping.create("1.3.8.BUILD-SNAPSHOT", "1.1.1-SNAPSHOT"));
|
||||
InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addBom("foo", bom).build();
|
||||
Info info = getInfo(metadata);
|
||||
assertThat(info.getDetails()).containsKeys("bom-ranges");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> ranges = (Map<String, Object>) info.getDetails()
|
||||
.get("bom-ranges");
|
||||
assertThat(ranges).containsOnlyKeys("foo");
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> foo = (Map<String, Object>) ranges.get("foo");
|
||||
assertThat(foo).containsExactly(
|
||||
entry("1.1.0", "Spring Boot >=1.3.0.RELEASE and <=1.3.8.RELEASE"),
|
||||
entry("1.1.1-SNAPSHOT", "Spring Boot >=1.3.8.BUILD-SNAPSHOT"));
|
||||
}
|
||||
|
||||
private static Info getInfo(InitializrMetadata metadata) {
|
||||
Info.Builder builder = new Info.Builder();
|
||||
new BomRangesInfoContributor(new SimpleInitializrMetadataProvider(metadata))
|
||||
.contribute(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.metric;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.actuate.metrics.repository.redis.RedisMetricRepository;
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import io.spring.initializr.actuate.metric.MetricsExportTests.Config;
|
||||
import io.spring.initializr.actuate.test.RedisRunning;
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent;
|
||||
import io.spring.initializr.generator.ProjectRequest;
|
||||
import io.spring.initializr.metadata.InitializrMetadata;
|
||||
import io.spring.initializr.metadata.InitializrMetadataBuilder;
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider;
|
||||
import io.spring.initializr.metadata.InitializrProperties;
|
||||
import io.spring.initializr.metadata.SimpleInitializrMetadataProvider;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Config.class, properties = {
|
||||
"spring.metrics.export.delayMillis:500", "spring.metrics.export.enabled:true",
|
||||
"initializr.metrics.prefix:test.prefix", "initializr.metrics.key:key.test" })
|
||||
public class MetricsExportTests {
|
||||
|
||||
@Rule
|
||||
public RedisRunning running = new RedisRunning();
|
||||
|
||||
@Autowired
|
||||
ProjectGenerationMetricsListener listener;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("writer")
|
||||
MetricWriter writer;
|
||||
|
||||
RedisMetricRepository repository;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
repository = (RedisMetricRepository) writer;
|
||||
repository.findAll().forEach(it -> {
|
||||
repository.reset(it.getName());
|
||||
});
|
||||
assertTrue("Metrics not empty", repository.count() == 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exportAndCheckMetricsExist() throws Exception {
|
||||
listener.onGeneratedProject(new ProjectGeneratedEvent(new ProjectRequest()));
|
||||
Thread.sleep(1000L);
|
||||
assertTrue("No metrics exported", repository.count() > 0);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@EnableConfigurationProperties(InitializrProperties.class)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
InitializrMetadataProvider initializrMetadataProvider(
|
||||
InitializrProperties properties) {
|
||||
InitializrMetadata metadata = InitializrMetadataBuilder
|
||||
.fromInitializrProperties(properties).build();
|
||||
return new SimpleInitializrMetadataProvider(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.metric;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import io.spring.initializr.actuate.test.MetricsAssert;
|
||||
import io.spring.initializr.actuate.test.TestCounterService;
|
||||
import io.spring.initializr.generator.ProjectFailedEvent;
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent;
|
||||
import io.spring.initializr.generator.ProjectRequest;
|
||||
import io.spring.initializr.metadata.Dependency;
|
||||
import io.spring.initializr.metadata.InitializrMetadata;
|
||||
import io.spring.initializr.test.metadata.InitializrMetadataTestBuilder;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ProjectGenerationMetricsListenerTests {
|
||||
|
||||
private InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addDependencyGroup("core", "web", "security", "spring-data").build();
|
||||
|
||||
private ProjectGenerationMetricsListener listener;
|
||||
private MetricsAssert metricsAssert;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
TestCounterService counterService = new TestCounterService();
|
||||
listener = new ProjectGenerationMetricsListener(counterService);
|
||||
metricsAssert = new MetricsAssert(counterService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectGenerationCount() {
|
||||
ProjectRequest request = initialize();
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.requests");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void projectGenerationCountWithFailure() {
|
||||
ProjectRequest request = initialize();
|
||||
request.resolve(metadata);
|
||||
fireProjectFailedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.requests");
|
||||
metricsAssert.hasValue(1, "initializr.failures");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dependencies() {
|
||||
ProjectRequest request = initialize();
|
||||
request.getStyle().addAll(Arrays.asList("security", "spring-data"));
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.dependency.security",
|
||||
"initializr.dependency.spring-data");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noDependencies() {
|
||||
ProjectRequest request = initialize();
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasNoValue("initializr.dependency.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolvedWebDependency() {
|
||||
ProjectRequest request = initialize();
|
||||
request.getStyle().add("spring-data");
|
||||
request.setPackaging("war");
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.dependency.web",
|
||||
"initializr.dependency.spring-data");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aliasedDependencyUseStandardId() {
|
||||
Dependency dependency = new Dependency();
|
||||
dependency.setId("foo");
|
||||
dependency.getAliases().add("foo-old");
|
||||
InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addDependencyGroup("core", dependency).build();
|
||||
ProjectRequest request = new ProjectRequest();
|
||||
request.initialize(metadata);
|
||||
request.getStyle().add("foo-old");
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.dependency.foo"); // standard id is used
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultType() {
|
||||
ProjectRequest request = initialize();
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.type.maven-project");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitType() {
|
||||
ProjectRequest request = initialize();
|
||||
request.setType("gradle-build");
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.type.gradle-build");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultPackaging() {
|
||||
ProjectRequest request = initialize();
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.packaging.jar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitPackaging() {
|
||||
ProjectRequest request = initialize();
|
||||
request.setPackaging("war");
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.packaging.war");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultJavaVersion() {
|
||||
ProjectRequest request = initialize();
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.java_version.1_8");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitJavaVersion() {
|
||||
ProjectRequest request = initialize();
|
||||
request.setJavaVersion("1.7");
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.java_version.1_7");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultLanguage() {
|
||||
ProjectRequest request = initialize();
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.language.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitGroovyLanguage() {
|
||||
ProjectRequest request = initialize();
|
||||
request.setLanguage("groovy");
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.language.groovy");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitKotlinLanguage() {
|
||||
ProjectRequest request = initialize();
|
||||
request.setLanguage("kotlin");
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.language.kotlin");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultBootVersion() {
|
||||
ProjectRequest request = initialize();
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.boot_version.1_2_3_RELEASE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitBootVersion() {
|
||||
ProjectRequest request = initialize();
|
||||
request.setBootVersion("1.0.2.RELEASE");
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.boot_version.1_0_2_RELEASE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userAgentAvailable() {
|
||||
ProjectRequest request = initialize();
|
||||
request.getParameters().put("user-agent", "HTTPie/0.9.2");
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.client_id.httpie");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void collectAllMetrics() {
|
||||
ProjectRequest request = initialize();
|
||||
request.getStyle().addAll(Arrays.asList("web", "security"));
|
||||
request.setType("gradle-project");
|
||||
request.setPackaging("jar");
|
||||
request.setJavaVersion("1.6");
|
||||
request.setLanguage("groovy");
|
||||
request.setBootVersion("1.0.2.RELEASE");
|
||||
request.getParameters().put("user-agent", "SpringBootCli/1.3.0.RELEASE");
|
||||
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.requests",
|
||||
"initializr.dependency.web", "initializr.dependency.security",
|
||||
"initializr.type.gradle-project", "initializr.packaging.jar",
|
||||
"initializr.java_version.1_6", "initializr.language.groovy",
|
||||
"initializr.boot_version.1_0_2_RELEASE",
|
||||
"initializr.client_id.spring").metricsCount(9);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incrementMetrics() {
|
||||
ProjectRequest request = initialize();
|
||||
request.getStyle().addAll(Arrays.asList("security","spring-data"));
|
||||
request.resolve(metadata);
|
||||
fireProjectGeneratedEvent(request);
|
||||
metricsAssert.hasValue(1, "initializr.requests",
|
||||
"initializr.dependency.security", "initializr.dependency.spring-data");
|
||||
|
||||
ProjectRequest anotherRequest = initialize();
|
||||
anotherRequest.getStyle().addAll(Arrays.asList("web", "spring-data"));
|
||||
anotherRequest.resolve(metadata);
|
||||
fireProjectGeneratedEvent(anotherRequest);
|
||||
metricsAssert.hasValue(2, "initializr.dependency.spring-data",
|
||||
"initializr.dependency.spring-data");
|
||||
metricsAssert.hasValue(1, "initializr.dependency.web",
|
||||
"initializr.dependency.security");
|
||||
}
|
||||
|
||||
private void fireProjectGeneratedEvent(ProjectRequest projectRequest) {
|
||||
listener.onGeneratedProject(new ProjectGeneratedEvent(projectRequest));
|
||||
}
|
||||
|
||||
private void fireProjectFailedEvent(ProjectRequest projectRequest) {
|
||||
listener.onFailedProject(new ProjectFailedEvent(projectRequest, null));
|
||||
}
|
||||
|
||||
private ProjectRequest initialize() {
|
||||
ProjectRequest request = new ProjectRequest();
|
||||
request.initialize(metadata);
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,34 +14,35 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.actuate.stat
|
||||
package io.spring.initializr.actuate.stat;
|
||||
|
||||
import io.spring.initializr.generator.ProjectRequest
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider
|
||||
import io.spring.initializr.metadata.SimpleInitializrMetadataProvider
|
||||
import io.spring.initializr.test.metadata.InitializrMetadataTestBuilder
|
||||
import io.spring.initializr.generator.ProjectRequest;
|
||||
import io.spring.initializr.metadata.InitializrMetadata;
|
||||
import io.spring.initializr.metadata.InitializrMetadataProvider;
|
||||
import io.spring.initializr.metadata.SimpleInitializrMetadataProvider;
|
||||
import io.spring.initializr.test.metadata.InitializrMetadataTestBuilder;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
abstract class AbstractInitializrStatTests {
|
||||
|
||||
def metadata = InitializrMetadataTestBuilder
|
||||
InitializrMetadata metadata = InitializrMetadataTestBuilder
|
||||
.withDefaults()
|
||||
.addDependencyGroup('core', 'security', 'validation', 'aop')
|
||||
.addDependencyGroup('web', 'web', 'data-rest', 'jersey')
|
||||
.addDependencyGroup('data', 'data-jpa', 'jdbc')
|
||||
.addDependencyGroup('database', 'h2', 'mysql')
|
||||
.build()
|
||||
.addDependencyGroup("core", "security", "validation", "aop")
|
||||
.addDependencyGroup("web", "web", "data-rest", "jersey")
|
||||
.addDependencyGroup("data", "data-jpa", "jdbc")
|
||||
.addDependencyGroup("database", "h2", "mysql")
|
||||
.build();
|
||||
|
||||
protected InitializrMetadataProvider createProvider(def metadata) {
|
||||
new SimpleInitializrMetadataProvider(metadata)
|
||||
protected InitializrMetadataProvider createProvider(InitializrMetadata metadata) {
|
||||
return new SimpleInitializrMetadataProvider(metadata);
|
||||
}
|
||||
|
||||
protected ProjectRequest createProjectRequest() {
|
||||
ProjectRequest request = new ProjectRequest()
|
||||
request.initialize(metadata)
|
||||
request
|
||||
ProjectRequest request = new ProjectRequest();
|
||||
request.initialize(metadata);
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.stat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
|
||||
import groovy.json.JsonSlurper;
|
||||
import io.spring.initializr.actuate.stat.MainControllerStatsIntegrationTests.StatsMockController;
|
||||
import io.spring.initializr.actuate.stat.MainControllerStatsIntegrationTests.StatsMockController.Content;
|
||||
import io.spring.initializr.web.AbstractFullStackInitializrIntegrationTests;
|
||||
|
||||
/**
|
||||
* Integration tests for stats processing.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Import(StatsMockController.class)
|
||||
@ActiveProfiles({ "test-default", "test-custom-stats" })
|
||||
public class MainControllerStatsIntegrationTests
|
||||
extends AbstractFullStackInitializrIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private StatsMockController statsMockController;
|
||||
|
||||
@Autowired
|
||||
private StatsProperties statsProperties;
|
||||
|
||||
private final JsonSlurper slurper = new JsonSlurper();
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.statsMockController.stats.clear();
|
||||
// Make sure our mock is going to be invoked with the stats
|
||||
this.statsProperties.getElastic().setUri( "http://localhost:" + port + "/elastic");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleProject() {
|
||||
downloadArchive("/starter.zip?groupId=com.foo&artifactId=bar&dependencies=web");
|
||||
assertEquals("No stat got generated", 1, statsMockController.stats.size());
|
||||
Content content = statsMockController.stats.get(0);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String,Object> json = (Map<String, Object>) slurper.parseText(content.json);
|
||||
assertEquals("com.foo", json.get("groupId"));
|
||||
assertEquals("bar", json.get("artifactId"));
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> list = (List<String>) json.get("dependencies");
|
||||
assertEquals(1, list.size());
|
||||
assertEquals("web", list.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authorizationHeaderIsSet() {
|
||||
downloadArchive("/starter.zip");
|
||||
assertEquals("No stat got generated", 1, statsMockController.stats.size());
|
||||
Content content = statsMockController.stats.get(0);
|
||||
|
||||
String authorization = content.authorization;
|
||||
assertNotNull("Authorization header must be set", authorization);
|
||||
assertTrue("Wrong value for authorization header", authorization.startsWith("Basic "));
|
||||
String token = authorization.substring("Basic ".length(), authorization.length());
|
||||
String[] data = new String(Base64Utils.decodeFromString(token)).split(":");
|
||||
assertEquals("Wrong user from $token", "test-user", data[0]);
|
||||
assertEquals("Wrong password $token", "test-password", data[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestIpNotSetByDefault() {
|
||||
downloadArchive("/starter.zip?groupId=com.foo&artifactId=bar&dependencies=web");
|
||||
assertEquals("No stat got generated", 1, statsMockController.stats.size());
|
||||
Content content = statsMockController.stats.get(0);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String,Object> json = (Map<String, Object>) slurper.parseText(content.json);
|
||||
assertFalse("requestIp property should not be set", json.containsKey("requestIp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestIpIsSetWhenHeaderIsPresent() throws Exception {
|
||||
RequestEntity<?> request = RequestEntity.get(new URI(createUrl("/starter.zip")))
|
||||
.header("X-FORWARDED-FOR", "10.0.0.123").build();
|
||||
getRestTemplate().exchange(request, String.class);
|
||||
assertEquals("No stat got generated", 1, statsMockController.stats.size());
|
||||
Content content = statsMockController.stats.get(0);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String,Object> json = (Map<String, Object>) slurper.parseText(content.json);
|
||||
assertEquals("Wrong requestIp", "10.0.0.123", json.get("requestIp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestIpv4IsNotSetWhenHeaderHasGarbage() throws Exception {
|
||||
RequestEntity<?> request = RequestEntity.get(new URI(createUrl("/starter.zip")))
|
||||
.header("x-forwarded-for", "foo-bar").build();
|
||||
getRestTemplate().exchange(request, String.class);
|
||||
assertEquals("No stat got generated", 1, statsMockController.stats.size());
|
||||
Content content = statsMockController.stats.get(0);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String,Object> json = (Map<String, Object>) slurper.parseText(content.json);
|
||||
assertFalse("requestIpv4 property should not be set if value is not a valid IPv4",
|
||||
json.containsKey("requestIpv4"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestCountryIsNotSetWhenHeaderIsSetToXX() throws Exception {
|
||||
RequestEntity<?> request = RequestEntity.get(new URI(createUrl("/starter.zip")))
|
||||
.header("cf-ipcountry", "XX").build();
|
||||
getRestTemplate().exchange(request, String.class);
|
||||
assertEquals("No stat got generated", 1, statsMockController.stats.size());
|
||||
Content content = statsMockController.stats.get(0);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String,Object> json = (Map<String, Object>) slurper.parseText(content.json);
|
||||
assertFalse("requestCountry property should not be set if value is set to xx",
|
||||
json.containsKey("requestCountry"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidProjectSillHasStats() {
|
||||
try {
|
||||
downloadArchive("/starter.zip?type=invalid-type");
|
||||
fail("Should have failed to generate project with invalid type");
|
||||
} catch (HttpClientErrorException ex) {
|
||||
assertEquals(HttpStatus.BAD_REQUEST, ex.getStatusCode());
|
||||
}
|
||||
assertEquals("No stat got generated", 1, statsMockController.stats.size());
|
||||
Content content = statsMockController.stats.get(0);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String,Object> json = (Map<String, Object>) slurper.parseText(content.json);
|
||||
assertEquals("com.example", json.get("groupId"));
|
||||
assertEquals("demo", json.get("artifactId"));
|
||||
assertEquals(true, json.get("invalid"));
|
||||
assertEquals(true, json.get("invalidType"));
|
||||
assertNotNull(json.get("errorMessage"));
|
||||
assertTrue(((String) json.get("errorMessage")).contains("invalid-type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void errorPublishingStatsDoesNotBubbleUp() {
|
||||
this.statsProperties.getElastic()
|
||||
.setUri("http://localhost:" + port + "/elastic-error");
|
||||
downloadArchive("/starter.zip");
|
||||
assertEquals("No stat should be available", 0, statsMockController.stats.size());
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class StatsMockController {
|
||||
|
||||
private final List<Content> stats = new ArrayList<>();
|
||||
|
||||
@RequestMapping(path = "/elastic/test/my-entity", method = RequestMethod.POST)
|
||||
void handleProjectRequestDocument(RequestEntity<String> input) {
|
||||
String authorization = input.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
Content content = new Content(authorization, input.getBody());
|
||||
this.stats.add(content);
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/elastic-error/test/my-entity", method = RequestMethod.POST)
|
||||
void handleExpectedError() {
|
||||
throw new IllegalStateException("Expected exception");
|
||||
}
|
||||
|
||||
static class Content {
|
||||
|
||||
public Content(String authorization, String body) {
|
||||
this.authorization = authorization;
|
||||
json = body;
|
||||
}
|
||||
|
||||
String authorization;
|
||||
|
||||
String json;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.stat;
|
||||
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
|
||||
import io.spring.initializr.actuate.stat.StatsProperties.Elastic;
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent;
|
||||
import io.spring.initializr.generator.ProjectRequest;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ProjectGenerationStatPublisherTests extends AbstractInitializrStatTests {
|
||||
|
||||
private StatsProperties properties;
|
||||
private RetryTemplate retryTemplate;
|
||||
private ProjectGenerationStatPublisher statPublisher;
|
||||
private MockRestServiceServer mockServer;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.properties = createProperties();
|
||||
ProjectRequestDocumentFactory documentFactory =
|
||||
new ProjectRequestDocumentFactory(createProvider(metadata));
|
||||
this.retryTemplate = new RetryTemplate();
|
||||
this.statPublisher = new ProjectGenerationStatPublisher(documentFactory, properties, retryTemplate);
|
||||
mockServer = MockRestServiceServer.createServer(this.statPublisher.getRestTemplate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publishSimpleDocument() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.setGroupId("com.example.foo");
|
||||
request.setArtifactId("my-project");
|
||||
|
||||
mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andExpect(jsonPath("$.groupId").value("com.example.foo"))
|
||||
.andExpect(jsonPath("$.artifactId").value("my-project"))
|
||||
.andRespond(withStatus(HttpStatus.CREATED)
|
||||
.body(mockResponse(UUID.randomUUID().toString(), true))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
);
|
||||
|
||||
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request));
|
||||
mockServer.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recoverFromError() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
|
||||
mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
|
||||
|
||||
mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
|
||||
|
||||
mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.CREATED)
|
||||
.body(mockResponse(UUID.randomUUID().toString(), true))
|
||||
.contentType(MediaType.APPLICATION_JSON));
|
||||
|
||||
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request));
|
||||
mockServer.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fatalErrorOnlyLogs() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
this.retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2,
|
||||
Collections.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)));
|
||||
|
||||
mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
|
||||
|
||||
mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
|
||||
.andExpect(method(HttpMethod.POST))
|
||||
.andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR));
|
||||
|
||||
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request));
|
||||
mockServer.verify();
|
||||
}
|
||||
|
||||
private static String mockResponse(String id, boolean created) {
|
||||
return "{\"_index\":\"initializr\",\"_type\":\"request\",\"_id\":\"" + id + "\",\"_version\":1,\"_shards\"" +
|
||||
":{\"total\":1,\"successful\":1,\"failed\":0},\"created\":" + created + "}";
|
||||
}
|
||||
|
||||
private static StatsProperties createProperties() {
|
||||
StatsProperties properties = new StatsProperties();
|
||||
Elastic elastic = properties.getElastic();
|
||||
elastic.setUri("http://example.com/elastic");
|
||||
elastic.setUsername("foo");
|
||||
elastic.setPassword("bar");
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.stat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import io.spring.initializr.generator.ProjectFailedEvent;
|
||||
import io.spring.initializr.generator.ProjectGeneratedEvent;
|
||||
import io.spring.initializr.generator.ProjectRequest;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
|
||||
|
||||
private final ProjectRequestDocumentFactory factory =
|
||||
new ProjectRequestDocumentFactory(createProvider(metadata));
|
||||
|
||||
@Test
|
||||
public void createDocumentForSimpleProject() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals(event.getTimestamp(), document.getGenerationTimestamp());
|
||||
assertEquals(null, document.getRequestIp());
|
||||
assertEquals("com.example", document.getGroupId());
|
||||
assertEquals("demo", document.getArtifactId());
|
||||
assertEquals("com.example", document.getPackageName());
|
||||
assertEquals("1.2.3.RELEASE", document.getBootVersion());
|
||||
assertEquals("1.8", document.getJavaVersion());
|
||||
assertEquals("java", document.getLanguage());
|
||||
assertEquals("jar", document.getPackaging());
|
||||
assertEquals("maven-project", document.getType());
|
||||
assertEquals(0, document.getDependencies().size());
|
||||
assertValid(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentWithRequestIp() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.getParameters().put("x-forwarded-for","10.0.0.123");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("10.0.0.123", document.getRequestIp());
|
||||
assertEquals("10.0.0.123", document.getRequestIpv4());
|
||||
assertNull(document.getRequestCountry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentWithRequestIpv6() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.getParameters().put("x-forwarded-for", "2001:db8:a0b:12f0::1");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("2001:db8:a0b:12f0::1", document.getRequestIp());
|
||||
assertNull(document.getRequestIpv4());
|
||||
assertNull(document.getRequestCountry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentWithCloudFlareHeaders() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.getParameters().put("cf-connecting-ip", "10.0.0.123");
|
||||
request.getParameters().put("cf-ipcountry","BE");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("10.0.0.123", document.getRequestIp());
|
||||
assertEquals("10.0.0.123", document.getRequestIpv4());
|
||||
assertEquals("BE", document.getRequestCountry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentWithCloudFlareIpv6() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.getParameters().put("cf-connecting-ip", "2001:db8:a0b:12f0::1");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("2001:db8:a0b:12f0::1", document.getRequestIp());
|
||||
assertNull(document.getRequestIpv4());
|
||||
assertNull(document.getRequestCountry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentWithCloudFlareHeadersAndOtherHeaders() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.getParameters().put("cf-connecting-ip", "10.0.0.123");
|
||||
request.getParameters().put("x-forwarded-for", "192.168.1.101");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("10.0.0.123", document.getRequestIp());
|
||||
assertEquals("10.0.0.123", document.getRequestIpv4());
|
||||
assertNull(document.getRequestCountry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentWithCloudFlareCountrySetToXX() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.getParameters().put("cf-connecting-ip", "Xx"); // case insensitive
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertNull(document.getRequestCountry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentWithUserAgent() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.getParameters().put("user-agent","HTTPie/0.8.0");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("httpie", document.getClientId());
|
||||
assertEquals("0.8.0", document.getClientVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentWithUserAgentNoVersion() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.getParameters().put("user-agent","IntelliJ IDEA");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("intellijidea", document.getClientId());
|
||||
assertEquals(null, document.getClientVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentInvalidJavaVersion() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.setJavaVersion("1.2");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("1.2", document.getJavaVersion());
|
||||
assertTrue(document.isInvalid());
|
||||
assertTrue(document.isInvalidJavaVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentInvalidLanguage() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.setLanguage("c++");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("c++", document.getLanguage());
|
||||
assertTrue(document.isInvalid());
|
||||
assertTrue(document.isInvalidLanguage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentInvalidPackaging() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.setPackaging("ear");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("ear", document.getPackaging());
|
||||
assertTrue(document.isInvalid());
|
||||
assertTrue(document.isInvalidPackaging());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentInvalidType() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.setType("ant-project");
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("ant-project", document.getType());
|
||||
assertTrue(document.isInvalid());
|
||||
assertTrue(document.isInvalidType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentInvalidDependency() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
request.setDependencies(Arrays.asList("web", "invalid", "data-jpa", "invalid-2"));
|
||||
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertEquals("web", document.getDependencies().get(0));
|
||||
assertEquals("data-jpa", document.getDependencies().get(1));
|
||||
assertEquals(2, document.getDependencies().size());
|
||||
assertTrue(document.isInvalid());
|
||||
assertEquals("invalid", document.getInvalidDependencies().get(0));
|
||||
assertEquals("invalid-2", document.getInvalidDependencies().get(1));
|
||||
assertEquals(2, document.getInvalidDependencies().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDocumentWithProjectFailedEvent() {
|
||||
ProjectRequest request = createProjectRequest();
|
||||
ProjectFailedEvent event = new ProjectFailedEvent(request, new IllegalStateException("my test message"));
|
||||
ProjectRequestDocument document = factory.createDocument(event);
|
||||
assertTrue(document.isInvalid());
|
||||
assertEquals("my test message", document.getErrorMessage());
|
||||
}
|
||||
|
||||
private static void assertValid(ProjectRequestDocument document) {
|
||||
assertFalse(document.isInvalid());
|
||||
assertFalse(document.isInvalidJavaVersion());
|
||||
assertFalse(document.isInvalidLanguage());
|
||||
assertFalse(document.isInvalidPackaging());
|
||||
assertEquals(0, document.getInvalidDependencies().size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,34 +14,34 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.actuate.stat
|
||||
package io.spring.initializr.actuate.stat;
|
||||
|
||||
import io.spring.initializr.actuate.stat.StatsProperties
|
||||
import org.junit.Test
|
||||
import io.spring.initializr.actuate.stat.StatsProperties;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertThat
|
||||
import static org.hamcrest.CoreMatchers.is
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class StatsPropertiesTests {
|
||||
public class StatsPropertiesTests {
|
||||
|
||||
private final StatsProperties properties = new StatsProperties()
|
||||
private final StatsProperties properties = new StatsProperties();
|
||||
|
||||
@Test
|
||||
void cleanTrailingSlash() {
|
||||
properties.elastic.uri = 'http://example.com/'
|
||||
assertThat(properties.elastic.uri, is('http://example.com'))
|
||||
public void cleanTrailingSlash() {
|
||||
properties.getElastic().setUri("http://example.com/");
|
||||
assertThat(properties.getElastic().getUri(), is("http://example.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void provideEntityUrl() {
|
||||
properties.elastic.uri = 'http://example.com/'
|
||||
properties.elastic.indexName = 'my-index'
|
||||
properties.elastic.entityName = 'foo'
|
||||
assertThat(properties.elastic.entityUrl.toString(),
|
||||
is('http://example.com/my-index/foo'))
|
||||
public void provideEntityUrl() {
|
||||
properties.getElastic().setUri("http://example.com/");
|
||||
properties.getElastic().setIndexName("my-index");
|
||||
properties.getElastic().setEntityName("foo");
|
||||
assertThat(properties.getElastic().getEntityUrl().toString(),
|
||||
is("http://example.com/my-index/foo"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.actuate.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Metrics assertion based on {@link TestCounterService}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class MetricsAssert {
|
||||
|
||||
private final TestCounterService counterService;
|
||||
|
||||
public MetricsAssert(TestCounterService counterService) {
|
||||
this.counterService = counterService;
|
||||
}
|
||||
|
||||
public MetricsAssert hasValue(long value, String... metrics) {
|
||||
Arrays.asList(metrics).forEach(it -> {
|
||||
Long actual = counterService.values.get(it);
|
||||
if (actual == null) {
|
||||
fail("Metric '" + it + "' not found, got '"
|
||||
+ counterService.values.keySet() + "'");
|
||||
}
|
||||
assertEquals("Wrong value for metric " + it, value, actual.longValue());
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public MetricsAssert hasNoValue(String... metrics) {
|
||||
Arrays.asList(metrics).forEach(it -> {
|
||||
assertEquals("Metric '" + it + "' should not be registered", null,
|
||||
counterService.values.get(it));
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public MetricsAssert metricsCount(int count) {
|
||||
assertEquals(
|
||||
"Wrong number of metrics, got '" + counterService.values.keySet() + "'",
|
||||
count, counterService.values.size());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -14,36 +14,35 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.actuate.test
|
||||
package io.spring.initializr.actuate.test;
|
||||
|
||||
import org.junit.Assume
|
||||
import org.junit.rules.TestWatcher
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory
|
||||
import org.junit.Assume;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
|
||||
/**
|
||||
* A {@link org.junit.rules.TestRule} that validates Redis is available.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
class RedisRunning extends TestWatcher {
|
||||
public class RedisRunning extends TestWatcher {
|
||||
|
||||
JedisConnectionFactory connectionFactory;
|
||||
|
||||
@Override
|
||||
Statement apply(Statement base, Description description) {
|
||||
public Statement apply(Statement base, Description description) {
|
||||
if (connectionFactory == null) {
|
||||
connectionFactory = new JedisConnectionFactory()
|
||||
connectionFactory.afterPropertiesSet()
|
||||
connectionFactory = new JedisConnectionFactory();
|
||||
connectionFactory.afterPropertiesSet();
|
||||
}
|
||||
try {
|
||||
connectionFactory.connection
|
||||
connectionFactory.getConnection();
|
||||
} catch (Exception e) {
|
||||
Assume.assumeNoException('Cannot connect to Redis (so skipping tests)', e)
|
||||
Assume.assumeNoException("Cannot connect to Redis (so skipping tests)", e);
|
||||
}
|
||||
super.apply(base, description)
|
||||
return super.apply(base, description);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,36 +14,39 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.initializr.actuate.test
|
||||
package io.spring.initializr.actuate.test;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.CounterService
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.CounterService;
|
||||
|
||||
/**
|
||||
* A test {@link CounterService} that keeps track of the metric values.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class TestCounterService implements CounterService {
|
||||
public class TestCounterService implements CounterService {
|
||||
|
||||
final Map<String, Long> values = [:]
|
||||
final Map<String, Long> values = new HashMap<>();
|
||||
|
||||
@Override
|
||||
void increment(String metricName) {
|
||||
def value = values[metricName]
|
||||
def valueToSet = value ? ++value : 1
|
||||
values[metricName] = valueToSet
|
||||
public void increment(String metricName) {
|
||||
Long value = values.get(metricName);
|
||||
Long valueToSet = value!=null ? ++value : 1;
|
||||
values.put(metricName, valueToSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
void decrement(String metricName) {
|
||||
def value = values[metricName]
|
||||
def valueToSet = value ? --value : -1
|
||||
values[metricName] = valueToSet
|
||||
public void decrement(String metricName) {
|
||||
Long value = values.get(metricName);
|
||||
Long valueToSet = value!=null ? +--value : -1;
|
||||
values.put(metricName, valueToSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
void reset(String metricName) {
|
||||
values[metricName] = 0
|
||||
public void reset(String metricName) {
|
||||
values.put(metricName, 0L);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,7 +39,7 @@ class ProjectRequest extends BasicProjectRequest {
|
||||
/**
|
||||
* The id of the starter to use if no dependency is defined.
|
||||
*/
|
||||
static final DEFAULT_STARTER = 'root_starter'
|
||||
public static final DEFAULT_STARTER = 'root_starter'
|
||||
|
||||
/**
|
||||
* Additional parameters that can be used to further identify the request.
|
||||
|
||||
@@ -29,12 +29,12 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = Config.class, webEnvironment = RANDOM_PORT)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||
abstract class AbstractFullStackInitializrIntegrationTests
|
||||
extends AbstractInitializrIntegrationTests {
|
||||
|
||||
@LocalServerPort
|
||||
int port
|
||||
protected int port
|
||||
|
||||
String host = "localhost"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user