Improve stat document structure

Closers gh-787
This commit is contained in:
Stephane Nicoll
2018-12-28 14:46:49 +01:00
parent 1f168651db
commit 6650dbad11
11 changed files with 776 additions and 274 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,15 @@
package io.spring.initializr.actuate.stat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;
import io.spring.initializr.util.Agent;
import io.spring.initializr.util.Version;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Define the statistics of a project generation.
@@ -28,47 +36,29 @@ public class ProjectRequestDocument {
private long generationTimestamp;
private String requestIp;
private String type;
private String requestIpv4;
private String requestCountry;
private String clientId;
private String clientVersion;
private String buildSystem;
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 String packageName;
private final List<String> dependencies = new ArrayList<>();
private VersionInformation version;
private String errorMessage;
private ClientInformation client;
private boolean invalid;
private DependencyInformation dependencies;
private boolean invalidJavaVersion;
private boolean invalidLanguage;
private boolean invalidPackaging;
private boolean invalidType;
private final List<String> invalidDependencies = new ArrayList<>();
private ErrorStateInformation errorState;
public long getGenerationTimestamp() {
return this.generationTimestamp;
@@ -78,44 +68,20 @@ public class ProjectRequestDocument {
this.generationTimestamp = generationTimestamp;
}
public String getRequestIp() {
return this.requestIp;
public String getType() {
return this.type;
}
public void setRequestIp(String requestIp) {
this.requestIp = requestIp;
public void setType(String type) {
this.type = type;
}
public String getRequestIpv4() {
return this.requestIpv4;
public String getBuildSystem() {
return this.buildSystem;
}
public void setRequestIpv4(String requestIpv4) {
this.requestIpv4 = requestIpv4;
}
public String getRequestCountry() {
return this.requestCountry;
}
public void setRequestCountry(String requestCountry) {
this.requestCountry = requestCountry;
}
public String getClientId() {
return this.clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientVersion() {
return this.clientVersion;
}
public void setClientVersion(String clientVersion) {
this.clientVersion = clientVersion;
public void setBuildSystem(String buildSystem) {
this.buildSystem = buildSystem;
}
public String getGroupId() {
@@ -134,22 +100,6 @@ public class ProjectRequestDocument {
this.artifactId = artifactId;
}
public String getPackageName() {
return this.packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String getBootVersion() {
return this.bootVersion;
}
public void setBootVersion(String bootVersion) {
this.bootVersion = bootVersion;
}
public String getJavaVersion() {
return this.javaVersion;
}
@@ -174,104 +124,305 @@ public class ProjectRequestDocument {
this.packaging = packaging;
}
public String getType() {
return this.type;
public String getPackageName() {
return this.packageName;
}
public void setType(String type) {
this.type = type;
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String getErrorMessage() {
return this.errorMessage;
public VersionInformation getVersion() {
return this.version;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
public void setVersion(VersionInformation version) {
this.version = version;
}
public boolean isInvalid() {
return this.invalid;
public ClientInformation getClient() {
return this.client;
}
public void setInvalid(boolean invalid) {
this.invalid = invalid;
public void setClient(ClientInformation client) {
this.client = client;
}
public boolean isInvalidJavaVersion() {
return this.invalidJavaVersion;
}
public void setInvalidJavaVersion(boolean invalidJavaVersion) {
this.invalidJavaVersion = invalidJavaVersion;
}
public boolean isInvalidLanguage() {
return this.invalidLanguage;
}
public void setInvalidLanguage(boolean invalidLanguage) {
this.invalidLanguage = invalidLanguage;
}
public boolean isInvalidPackaging() {
return this.invalidPackaging;
}
public void setInvalidPackaging(boolean invalidPackaging) {
this.invalidPackaging = invalidPackaging;
}
public boolean isInvalidType() {
return this.invalidType;
}
public void setInvalidType(boolean invalidType) {
this.invalidType = invalidType;
}
public List<String> getDependencies() {
public DependencyInformation getDependencies() {
return this.dependencies;
}
public List<String> getInvalidDependencies() {
return this.invalidDependencies;
public void setDependencies(DependencyInformation dependencies) {
this.dependencies = dependencies;
}
public ErrorStateInformation getErrorState() {
return this.errorState;
}
public ErrorStateInformation triggerError() {
if (this.errorState == null) {
this.errorState = new ErrorStateInformation();
}
return this.errorState;
}
@Override
public String toString() {
return "ProjectRequestDocument [generationTimestamp=" + this.generationTimestamp
+ ", "
+ ((this.requestIp != null) ? "requestIp=" + this.requestIp + ", " : "")
+ ((this.requestIpv4 != null) ? "requestIpv4=" + this.requestIpv4 + ", "
: "")
+ ((this.requestCountry != null)
? "requestCountry=" + this.requestCountry + ", " : "")
+ ((this.clientId != null) ? "clientId=" + this.clientId + ", " : "")
+ ((this.clientVersion != null)
? "clientVersion=" + this.clientVersion + ", " : "")
+ ((this.groupId != null) ? "groupId=" + this.groupId + ", " : "")
+ ((this.artifactId != null) ? "artifactId=" + this.artifactId + ", "
: "")
+ ((this.packageName != null) ? "packageName=" + this.packageName + ", "
: "")
+ ((this.bootVersion != null) ? "bootVersion=" + this.bootVersion + ", "
: "")
+ ((this.javaVersion != null) ? "javaVersion=" + this.javaVersion + ", "
: "")
+ ((this.language != null) ? "language=" + this.language + ", " : "")
+ ((this.packaging != null) ? "packaging=" + this.packaging + ", " : "")
+ ((this.type != null) ? "type=" + this.type + ", " : "")
+ ((this.dependencies != null)
? "dependencies=" + this.dependencies + ", " : "")
+ ((this.errorMessage != null)
? "errorMessage=" + this.errorMessage + ", " : "")
+ "invalid=" + this.invalid + ", invalidJavaVersion="
+ this.invalidJavaVersion + ", invalidLanguage=" + this.invalidLanguage
+ ", invalidPackaging=" + this.invalidPackaging + ", invalidType="
+ this.invalidType + ", " + ((this.invalidDependencies != null)
? "invalidDependencies=" + this.invalidDependencies : "")
+ "]";
return new StringJoiner(", ", ProjectRequestDocument.class.getSimpleName() + "[",
"]").add("generationTimestamp=" + this.generationTimestamp)
.add("type='" + this.type + "'")
.add("buildSystem='" + this.buildSystem + "'")
.add("groupId='" + this.groupId + "'")
.add("artifactId='" + this.artifactId + "'")
.add("javaVersion='" + this.javaVersion + "'")
.add("language='" + this.language + "'")
.add("packaging='" + this.packaging + "'")
.add("packageName='" + this.packageName + "'")
.add("version=" + this.version).add("client=" + this.client)
.add("dependencies=" + this.dependencies)
.add("errorState=" + this.errorState).toString();
}
/**
* Spring Boot version information.
*/
public static class VersionInformation {
private final String id;
private final String major;
private final String minor;
public VersionInformation(Version version) {
this.id = version.toString();
this.major = String.format("%s", version.getMajor());
this.minor = (version.getMinor() != null)
? String.format("%s.%s", version.getMajor(), version.getMinor())
: null;
}
public String getId() {
return this.id;
}
public String getMajor() {
return this.major;
}
public String getMinor() {
return this.minor;
}
@Override
public String toString() {
return new StringJoiner(", ", "{", "}").add("id='" + this.id + "'")
.add("major='" + this.major + "'").add("minor='" + this.minor + "'")
.toString();
}
}
/**
* Dependencies information.
*/
public static class DependencyInformation {
private final String id;
private final List<String> values;
private final int count;
public DependencyInformation(List<String> values) {
this.id = computeDependenciesId(new ArrayList<>(values));
this.values = values;
this.count = values.size();
}
public String getId() {
return this.id;
}
public List<String> getValues() {
return this.values;
}
public int getCount() {
return this.count;
}
private static String computeDependenciesId(List<String> dependencies) {
if (ObjectUtils.isEmpty(dependencies)) {
return "_none";
}
Collections.sort(dependencies);
return StringUtils.collectionToDelimitedString(dependencies, " ");
}
@Override
public String toString() {
return new StringJoiner(", ", "{", "}").add("id='" + this.id + "'")
.add("values=" + this.values).add("count=" + this.count).toString();
}
}
/**
* Client information.
*/
public static class ClientInformation {
private final String id;
private final String version;
private final String ip;
private final String country;
public ClientInformation(Agent agent, String ip, String country) {
this.id = (agent != null) ? agent.getId().getId() : null;
this.version = (agent != null) ? agent.getVersion() : null;
this.ip = ip;
this.country = country;
}
public String getId() {
return this.id;
}
public String getVersion() {
return this.version;
}
public String getIp() {
return this.ip;
}
public String getCountry() {
return this.country;
}
@Override
public String toString() {
return new StringJoiner(", ", "{", "}").add("id='" + this.id + "'")
.add("version='" + this.version + "'").add("ip='" + this.ip + "'")
.add("country='" + this.country + "'").toString();
}
}
/**
* Additional information about what part of the request is invalid.
*/
public static class ErrorStateInformation {
private boolean invalid = true;
private Boolean javaVersion;
private Boolean language;
private Boolean packaging;
private Boolean type;
private InvalidDependencyInformation dependencies;
private String message;
public boolean isInvalid() {
return this.invalid;
}
public Boolean getJavaVersion() {
return this.javaVersion;
}
public void setJavaVersion(Boolean javaVersion) {
this.javaVersion = javaVersion;
}
public Boolean getLanguage() {
return this.language;
}
public void setLanguage(Boolean language) {
this.language = language;
}
public Boolean getPackaging() {
return this.packaging;
}
public void setPackaging(Boolean packaging) {
this.packaging = packaging;
}
public Boolean getType() {
return this.type;
}
public void setType(Boolean type) {
this.type = type;
}
public InvalidDependencyInformation getDependencies() {
return this.dependencies;
}
public void triggerInvalidDependencies(List<String> dependencies) {
this.dependencies = new InvalidDependencyInformation(dependencies);
}
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return new StringJoiner(", ", "{", "}").add("invalid=" + this.invalid)
.add("javaVersion=" + this.javaVersion)
.add("language=" + this.language).add("packaging=" + this.packaging)
.add("type=" + this.type).add("dependencies=" + this.dependencies)
.add("message='" + this.message + "'").toString();
}
}
/**
* Invalid dependencies information.
*/
public static class InvalidDependencyInformation {
private boolean invalid = true;
private final List<String> values;
public InvalidDependencyInformation(List<String> values) {
this.values = values;
}
public boolean isInvalid() {
return this.invalid;
}
public List<String> getValues() {
return this.values;
}
@Override
public String toString() {
return new StringJoiner(", ", "{", "}").add(String.join(", ", this.values))
.toString();
}
}
}

View File

@@ -18,15 +18,19 @@ 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 java.util.stream.Collectors;
import io.spring.initializr.actuate.stat.ProjectRequestDocument.ClientInformation;
import io.spring.initializr.actuate.stat.ProjectRequestDocument.DependencyInformation;
import io.spring.initializr.actuate.stat.ProjectRequestDocument.ErrorStateInformation;
import io.spring.initializr.actuate.stat.ProjectRequestDocument.VersionInformation;
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;
import io.spring.initializr.util.Version;
import org.springframework.util.StringUtils;
@@ -37,9 +41,6 @@ import org.springframework.util.StringUtils;
*/
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) {
@@ -52,93 +53,88 @@ public class ProjectRequestDocumentFactory {
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.setVersion(determineVersionInformation(request));
document.setClient(determineClientInformation(request));
document.setJavaVersion(request.getJavaVersion());
if (StringUtils.hasText(request.getJavaVersion())
&& metadata.getJavaVersions().get(request.getJavaVersion()) == null) {
document.setInvalid(true);
document.setInvalidJavaVersion(true);
document.triggerError().setJavaVersion(true);
}
document.setLanguage(request.getLanguage());
if (StringUtils.hasText(request.getLanguage())
&& metadata.getLanguages().get(request.getLanguage()) == null) {
document.setInvalid(true);
document.setInvalidLanguage(true);
document.triggerError().setLanguage(true);
}
document.setPackaging(request.getPackaging());
if (StringUtils.hasText(request.getPackaging())
&& metadata.getPackagings().get(request.getPackaging()) == null) {
document.setInvalid(true);
document.setInvalidPackaging(true);
document.triggerError().setPackaging(true);
}
document.setType(request.getType());
document.setBuildSystem(determineBuildSystem(request));
if (StringUtils.hasText(request.getType())
&& metadata.getTypes().get(request.getType()) == null) {
document.setInvalid(true);
document.setInvalidType(true);
document.triggerError().setType(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);
}
});
List<String> validDependencies = dependencies.stream()
.filter((id) -> metadata.getDependencies().get(id) != null)
.collect(Collectors.toList());
document.setDependencies(new DependencyInformation(validDependencies));
List<String> invalidDependencies = dependencies.stream()
.filter((id) -> (!validDependencies.contains(id)))
.collect(Collectors.toList());
if (!invalidDependencies.isEmpty()) {
document.triggerError().triggerInvalidDependencies(invalidDependencies);
}
// Let's make sure that the document is flagged as invalid no matter what
if (event instanceof ProjectFailedEvent) {
ErrorStateInformation errorState = document.triggerError();
ProjectFailedEvent failed = (ProjectFailedEvent) event;
document.setInvalid(true);
if (failed.getCause() != null) {
document.setErrorMessage(failed.getCause().getMessage());
errorState.setMessage(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) && !"xx".equalsIgnoreCase(country)) {
document.setRequestCountry(country);
}
private String determineBuildSystem(ProjectRequest request) {
String type = request.getType();
String[] elements = type.split("-");
return (elements.length == 2) ? elements[0] : null;
}
private static Agent extractAgentInformation(ProjectRequest request) {
private VersionInformation determineVersionInformation(ProjectRequest request) {
Version version = Version.safeParse(request.getBootVersion());
if (version != null && version.getMajor() != null) {
return new VersionInformation(version);
}
return null;
}
private ClientInformation determineClientInformation(ProjectRequest request) {
Agent agent = determineAgent(request);
String ip = determineIp(request);
String country = determineCountry(request);
if (agent != null || ip != null || country != null) {
return new ClientInformation(agent, ip, country);
}
return null;
}
private Agent determineAgent(ProjectRequest request) {
String userAgent = (String) request.getParameters().get("user-agent");
if (StringUtils.hasText(userAgent)) {
return Agent.fromUserAgent(userAgent);
@@ -146,12 +142,16 @@ public class ProjectRequestDocumentFactory {
return null;
}
private static String extractIpv4(String candidate) {
if (StringUtils.hasText(candidate)) {
Matcher matcher = IP_PATTERN.matcher(candidate);
if (matcher.find()) {
return matcher.group();
}
private String determineIp(ProjectRequest request) {
String candidate = (String) request.getParameters().get("cf-connecting-ip");
return (StringUtils.hasText(candidate)) ? candidate
: (String) request.getParameters().get("x-forwarded-for");
}
private String determineCountry(ProjectRequest request) {
String candidate = (String) request.getParameters().get("cf-ipcountry");
if (StringUtils.hasText(candidate) && !"xx".equalsIgnoreCase(candidate)) {
return candidate;
}
return null;
}

View File

@@ -73,7 +73,7 @@ class MainControllerStatsIntegrationTests
JsonNode json = parseJson(content.json);
assertThat(json.get("groupId").textValue()).isEqualTo("com.foo");
assertThat(json.get("artifactId").textValue()).isEqualTo("bar");
JsonNode list = json.get("dependencies");
JsonNode list = json.get("dependencies").get("values");
assertThat(list).hasSize(1);
assertThat(list.get(0).textValue()).isEqualTo("web");
}
@@ -113,7 +113,7 @@ class MainControllerStatsIntegrationTests
StatsMockController.Content content = this.statsMockController.stats.get(0);
JsonNode json = parseJson(content.json);
assertThat(json.get("requestIp").textValue()).as("Wrong requestIp")
assertThat(json.get("client").get("ip").textValue()).as("Wrong requestIp")
.isEqualTo("10.0.0.123");
}
@@ -160,10 +160,12 @@ class MainControllerStatsIntegrationTests
JsonNode json = parseJson(content.json);
assertThat(json.get("groupId").textValue()).isEqualTo("com.example");
assertThat(json.get("artifactId").textValue()).isEqualTo("demo");
assertThat(json.get("invalid").booleanValue()).isEqualTo(true);
assertThat(json.get("invalidType").booleanValue()).isEqualTo(true);
assertThat(json.get("errorMessage")).isNotNull();
assertThat(json.get("errorMessage").textValue()).contains("invalid-type");
assertThat(json.has("errorState")).isTrue();
JsonNode errorState = json.get("errorState");
assertThat(errorState.get("invalid").booleanValue()).isEqualTo(true);
assertThat(errorState.get("type").booleanValue()).isEqualTo(true);
assertThat(errorState.get("message")).isNotNull();
assertThat(errorState.get("message").textValue()).contains("invalid-type");
}
@Test

View File

@@ -16,27 +16,40 @@
package io.spring.initializr.actuate.stat;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collections;
import java.util.UUID;
import io.spring.initializr.actuate.stat.StatsProperties.Elastic;
import io.spring.initializr.generator.ProjectGeneratedEvent;
import io.spring.initializr.generator.ProjectRequest;
import org.json.JSONException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.Customization;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.skyscreamer.jsonassert.comparator.CustomComparator;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.client.RequestMatcher;
import org.springframework.util.StreamUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
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;
@@ -127,13 +140,125 @@ class ProjectGenerationStatPublisherTests extends AbstractInitializrStatTests {
@Test
void publishDocument() {
ProjectRequest request = createProjectRequest();
request.setGroupId("com.example.foo");
request.setArtifactId("my-project");
request.setGroupId("com.example.acme");
request.setArtifactId("project");
request.setType("maven-project");
request.setBootVersion("2.1.1.RELEASE");
request.setDependencies(Arrays.asList("web", "data-jpa"));
request.setLanguage("java");
request.getParameters().put("user-agent", "curl/1.2.4");
request.getParameters().put("cf-connecting-ip", "10.0.0.42");
request.getParameters().put("cf-ipcountry", "BE");
this.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"))
.andExpect(json("stat/request-simple.json"))
.andRespond(withStatus(HttpStatus.CREATED)
.body(mockResponse(UUID.randomUUID().toString(), true))
.contentType(MediaType.APPLICATION_JSON));
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request));
this.mockServer.verify();
}
@Test
void publishDocumentWithNoClientInformation() {
ProjectRequest request = createProjectRequest();
request.setGroupId("com.example.acme");
request.setArtifactId("test");
request.setType("gradle-project");
request.setBootVersion("2.1.0.RELEASE");
request.setDependencies(Arrays.asList("web", "data-jpa"));
request.setLanguage("java");
this.mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
.andExpect(method(HttpMethod.POST))
.andExpect(json("stat/request-no-client.json"))
.andRespond(withStatus(HttpStatus.CREATED)
.body(mockResponse(UUID.randomUUID().toString(), true))
.contentType(MediaType.APPLICATION_JSON));
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request));
this.mockServer.verify();
}
@Test
void publishDocumentWithInvalidType() {
ProjectRequest request = createProjectRequest();
request.setGroupId("com.example.acme");
request.setArtifactId("test");
request.setType("not-a-type");
request.setBootVersion("2.1.0.RELEASE");
request.setDependencies(Arrays.asList("web", "data-jpa"));
request.setLanguage("java");
this.mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
.andExpect(method(HttpMethod.POST))
.andExpect(json("stat/request-invalid-type.json"))
.andRespond(withStatus(HttpStatus.CREATED)
.body(mockResponse(UUID.randomUUID().toString(), true))
.contentType(MediaType.APPLICATION_JSON));
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request));
this.mockServer.verify();
}
@Test
void publishDocumentWithInvalidLanguage() {
ProjectRequest request = createProjectRequest();
request.setGroupId("com.example.acme");
request.setArtifactId("test");
request.setType("gradle-project");
request.setBootVersion("2.1.0.RELEASE");
request.setDependencies(Arrays.asList("web", "data-jpa"));
request.setLanguage("c");
this.mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
.andExpect(method(HttpMethod.POST))
.andExpect(json("stat/request-invalid-language.json"))
.andRespond(withStatus(HttpStatus.CREATED)
.body(mockResponse(UUID.randomUUID().toString(), true))
.contentType(MediaType.APPLICATION_JSON));
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request));
this.mockServer.verify();
}
@Test
void publishDocumentWithInvalidJavaVersion() {
ProjectRequest request = createProjectRequest();
request.setGroupId("com.example.acme");
request.setArtifactId("test");
request.setType("gradle-project");
request.setBootVersion("2.1.0.RELEASE");
request.setDependencies(Arrays.asList("web", "data-jpa"));
request.setLanguage("java");
request.setJavaVersion("1.2");
this.mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
.andExpect(method(HttpMethod.POST))
.andExpect(json("stat/request-invalid-java-version.json"))
.andRespond(withStatus(HttpStatus.CREATED)
.body(mockResponse(UUID.randomUUID().toString(), true))
.contentType(MediaType.APPLICATION_JSON));
this.statPublisher.handleEvent(new ProjectGeneratedEvent(request));
this.mockServer.verify();
}
@Test
void publishDocumentWithInvalidDependencies() {
ProjectRequest request = createProjectRequest();
request.setGroupId("com.example.acme");
request.setArtifactId("test");
request.setType("gradle-project");
request.setBootVersion("2.1.0.RELEASE");
request.setDependencies(Arrays.asList("invalid-2", "web", "invalid-1"));
request.setLanguage("java");
this.mockServer.expect(requestTo("http://example.com/elastic/initializr/request"))
.andExpect(method(HttpMethod.POST))
.andExpect(json("stat/request-invalid-dependencies.json"))
.andRespond(withStatus(HttpStatus.CREATED)
.body(mockResponse(UUID.randomUUID().toString(), true))
.contentType(MediaType.APPLICATION_JSON));
@@ -196,4 +321,39 @@ class ProjectGenerationStatPublisherTests extends AbstractInitializrStatTests {
return properties;
}
private static RequestMatcher json(String location) {
return (request) -> {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
assertJsonContent(readJson(location), mockRequest.getBodyAsString());
};
}
private static String readJson(String location) {
try {
try (InputStream in = new ClassPathResource(location).getInputStream()) {
return StreamUtils.copyToString(in, StandardCharsets.UTF_8);
}
}
catch (IOException ex) {
throw new IllegalStateException("Fail to read json from " + location, ex);
}
}
private static void assertJsonContent(String expected, String actual) {
try {
JSONAssert.assertEquals(expected, actual, new CustomComparator(
JSONCompareMode.STRICT,
Customization.customization("generationTimestamp", (o1, o2) -> {
Instant timestamp = Instant.ofEpochMilli((long) o1);
return timestamp
.isAfter(Instant.now().minus(2, ChronoUnit.SECONDS))
&& timestamp.isBefore(Instant.now());
})));
}
catch (JSONException ex) {
throw new AssertionError(
"Failed to parse expected or actual JSON request content", ex);
}
}
}

View File

@@ -26,6 +26,8 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ProjectRequestDocumentFactory}.
*
* @author Stephane Nicoll
*/
class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
@@ -38,18 +40,23 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
ProjectRequest request = createProjectRequest();
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getGenerationTimestamp()).isEqualTo(event.getTimestamp());
assertThat(document.getRequestIp()).isEqualTo(null);
assertThat(document.getGroupId()).isEqualTo("com.example");
assertThat(document.getArtifactId()).isEqualTo("demo");
assertThat(document.getPackageName()).isEqualTo("com.example.demo");
assertThat(document.getBootVersion()).isEqualTo("2.1.1.RELEASE");
assertThat(document.getBuildSystem()).isEqualTo("maven");
assertThat(document.getClient()).isNull();
assertThat(document.getDependencies().getValues()).isEmpty();
assertThat(document.getDependencies().getId()).isEqualTo("_none");
assertThat(document.getDependencies().getCount()).isEqualTo(0);
assertThat(document.getErrorState()).isNull();
assertThat(document.getGenerationTimestamp()).isEqualTo(event.getTimestamp());
assertThat(document.getGroupId()).isEqualTo("com.example");
assertThat(document.getJavaVersion()).isEqualTo("1.8");
assertThat(document.getLanguage()).isEqualTo("java");
assertThat(document.getPackageName()).isEqualTo("com.example.demo");
assertThat(document.getPackaging()).isEqualTo("jar");
assertThat(document.getType()).isEqualTo("maven-project");
assertThat(document.getDependencies()).isEmpty();
assertValid(document);
assertThat(document.getVersion().getId()).isEqualTo("2.1.1.RELEASE");
assertThat(document.getVersion().getMajor()).isEqualTo("2");
assertThat(document.getVersion().getMinor()).isEqualTo("2.1");
}
@Test
@@ -58,9 +65,8 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
request.getParameters().put("x-forwarded-for", "10.0.0.123");
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getRequestIp()).isEqualTo("10.0.0.123");
assertThat(document.getRequestIpv4()).isEqualTo("10.0.0.123");
assertThat(document.getRequestCountry()).isNull();
assertThat(document.getClient().getIp()).isEqualTo("10.0.0.123");
assertThat(document.getClient().getCountry()).isNull();
}
@Test
@@ -69,9 +75,8 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
request.getParameters().put("x-forwarded-for", "2001:db8:a0b:12f0::1");
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getRequestIp()).isEqualTo("2001:db8:a0b:12f0::1");
assertThat(document.getRequestIpv4()).isNull();
assertThat(document.getRequestCountry()).isNull();
assertThat(document.getClient().getIp()).isEqualTo("2001:db8:a0b:12f0::1");
assertThat(document.getClient().getCountry()).isNull();
}
@Test
@@ -81,9 +86,8 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
request.getParameters().put("cf-ipcountry", "BE");
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getRequestIp()).isEqualTo("10.0.0.123");
assertThat(document.getRequestIpv4()).isEqualTo("10.0.0.123");
assertThat(document.getRequestCountry()).isEqualTo("BE");
assertThat(document.getClient().getIp()).isEqualTo("10.0.0.123");
assertThat(document.getClient().getCountry()).isEqualTo("BE");
}
@Test
@@ -92,9 +96,8 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
request.getParameters().put("cf-connecting-ip", "2001:db8:a0b:12f0::1");
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getRequestIp()).isEqualTo("2001:db8:a0b:12f0::1");
assertThat(document.getRequestIpv4()).isNull();
assertThat(document.getRequestCountry()).isNull();
assertThat(document.getClient().getIp()).isEqualTo("2001:db8:a0b:12f0::1");
assertThat(document.getClient().getCountry()).isNull();
}
@Test
@@ -104,9 +107,8 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
request.getParameters().put("x-forwarded-for", "192.168.1.101");
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getRequestIp()).isEqualTo("10.0.0.123");
assertThat(document.getRequestIpv4()).isEqualTo("10.0.0.123");
assertThat(document.getRequestCountry()).isNull();
assertThat(document.getClient().getIp()).isEqualTo("10.0.0.123");
assertThat(document.getClient().getCountry()).isNull();
}
@Test
@@ -115,7 +117,7 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
request.getParameters().put("cf-connecting-ip", "Xx"); // case insensitive
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getRequestCountry()).isNull();
assertThat(document.getClient().getCountry()).isNull();
}
@Test
@@ -124,8 +126,8 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
request.getParameters().put("user-agent", "HTTPie/0.8.0");
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getClientId()).isEqualTo("httpie");
assertThat(document.getClientVersion()).isEqualTo("0.8.0");
assertThat(document.getClient().getId()).isEqualTo("httpie");
assertThat(document.getClient().getVersion()).isEqualTo("0.8.0");
}
@Test
@@ -134,8 +136,8 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
request.getParameters().put("user-agent", "IntelliJ IDEA");
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getClientId()).isEqualTo("intellijidea");
assertThat(document.getClientVersion()).isEqualTo(null);
assertThat(document.getClient().getId()).isEqualTo("intellijidea");
assertThat(document.getClient().getVersion()).isNull();
}
@Test
@@ -145,8 +147,12 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getJavaVersion()).isEqualTo("1.2");
assertThat(document.isInvalid()).isTrue();
assertThat(document.isInvalidJavaVersion()).isTrue();
assertThat(document.getErrorState().isInvalid()).isTrue();
assertThat(document.getErrorState().getJavaVersion()).isTrue();
assertThat(document.getErrorState().getLanguage()).isNull();
assertThat(document.getErrorState().getPackaging()).isNull();
assertThat(document.getErrorState().getType()).isNull();
assertThat(document.getErrorState().getDependencies()).isNull();
}
@Test
@@ -156,8 +162,12 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getLanguage()).isEqualTo("c++");
assertThat(document.isInvalid()).isTrue();
assertThat(document.isInvalidLanguage()).isTrue();
assertThat(document.getErrorState().isInvalid()).isTrue();
assertThat(document.getErrorState().getJavaVersion()).isNull();
assertThat(document.getErrorState().getLanguage()).isTrue();
assertThat(document.getErrorState().getPackaging()).isNull();
assertThat(document.getErrorState().getType()).isNull();
assertThat(document.getErrorState().getDependencies()).isNull();
}
@Test
@@ -167,8 +177,12 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getPackaging()).isEqualTo("ear");
assertThat(document.isInvalid()).isTrue();
assertThat(document.isInvalidPackaging()).isTrue();
assertThat(document.getErrorState().isInvalid()).isTrue();
assertThat(document.getErrorState().getJavaVersion()).isNull();
assertThat(document.getErrorState().getLanguage()).isNull();
assertThat(document.getErrorState().getPackaging()).isTrue();
assertThat(document.getErrorState().getType()).isNull();
assertThat(document.getErrorState().getDependencies()).isNull();
}
@Test
@@ -178,8 +192,12 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getType()).isEqualTo("ant-project");
assertThat(document.isInvalid()).isTrue();
assertThat(document.isInvalidType()).isTrue();
assertThat(document.getErrorState().isInvalid()).isTrue();
assertThat(document.getErrorState().getJavaVersion()).isNull();
assertThat(document.getErrorState().getLanguage()).isNull();
assertThat(document.getErrorState().getPackaging()).isNull();
assertThat(document.getErrorState().getType()).isTrue();
assertThat(document.getErrorState().getDependencies()).isNull();
}
@Test
@@ -188,13 +206,17 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
request.setDependencies(Arrays.asList("web", "invalid", "data-jpa", "invalid-2"));
ProjectGeneratedEvent event = new ProjectGeneratedEvent(request);
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.getDependencies().get(0)).isEqualTo("web");
assertThat(document.getDependencies().get(1)).isEqualTo("data-jpa");
assertThat(document.getDependencies()).hasSize(2);
assertThat(document.isInvalid()).isTrue();
assertThat(document.getInvalidDependencies().get(0)).isEqualTo("invalid");
assertThat(document.getInvalidDependencies().get(1)).isEqualTo("invalid-2");
assertThat(document.getInvalidDependencies()).hasSize(2);
assertThat(document.getDependencies().getValues()).containsExactly("web",
"data-jpa");
assertThat(document.getErrorState().isInvalid()).isTrue();
assertThat(document.getErrorState().getJavaVersion()).isNull();
assertThat(document.getErrorState().getLanguage()).isNull();
assertThat(document.getErrorState().getPackaging()).isNull();
assertThat(document.getErrorState().getType()).isNull();
assertThat(document.getErrorState().getDependencies()).isNotNull();
assertThat(document.getErrorState().getDependencies().isInvalid()).isTrue();
assertThat(document.getErrorState().getDependencies().getValues())
.containsExactly("invalid", "invalid-2");
}
@Test
@@ -203,16 +225,13 @@ class ProjectRequestDocumentFactoryTests extends AbstractInitializrStatTests {
ProjectFailedEvent event = new ProjectFailedEvent(request,
new IllegalStateException("my test message"));
ProjectRequestDocument document = this.factory.createDocument(event);
assertThat(document.isInvalid()).isTrue();
assertThat(document.getErrorMessage()).isEqualTo("my test message");
}
private static void assertValid(ProjectRequestDocument document) {
assertThat(document.isInvalid()).isFalse();
assertThat(document.isInvalidJavaVersion()).isFalse();
assertThat(document.isInvalidLanguage()).isFalse();
assertThat(document.isInvalidPackaging()).isFalse();
assertThat(document.getInvalidDependencies()).isEmpty();
assertThat(document.getErrorState().isInvalid()).isTrue();
assertThat(document.getErrorState().getJavaVersion()).isNull();
assertThat(document.getErrorState().getLanguage()).isNull();
assertThat(document.getErrorState().getPackaging()).isNull();
assertThat(document.getErrorState().getType()).isNull();
assertThat(document.getErrorState().getDependencies()).isNull();
assertThat(document.getErrorState().getMessage()).isEqualTo("my test message");
}
}

View File

@@ -0,0 +1,33 @@
{
"generationTimestamp": 0,
"type": "gradle-project",
"buildSystem": "gradle",
"groupId": "com.example.acme",
"artifactId": "test",
"javaVersion": "1.8",
"language": "java",
"packaging": "jar",
"packageName": "com.example.acme.test",
"version": {
"id": "2.1.0.RELEASE",
"major": "2",
"minor": "2.1"
},
"dependencies": {
"id": "web",
"values": [
"web"
],
"count": 1
},
"errorState": {
"invalid": true,
"dependencies": {
"invalid": true,
"values": [
"invalid-2",
"invalid-1"
]
}
}
}

View File

@@ -0,0 +1,28 @@
{
"generationTimestamp": 0,
"type": "gradle-project",
"buildSystem": "gradle",
"groupId": "com.example.acme",
"artifactId": "test",
"javaVersion": "1.2",
"language": "java",
"packaging": "jar",
"packageName": "com.example.acme.test",
"version": {
"id": "2.1.0.RELEASE",
"major": "2",
"minor": "2.1"
},
"dependencies": {
"id": "data-jpa web",
"values": [
"web",
"data-jpa"
],
"count": 2
},
"errorState": {
"invalid": true,
"javaVersion": true
}
}

View File

@@ -0,0 +1,28 @@
{
"generationTimestamp": 0,
"type": "gradle-project",
"buildSystem": "gradle",
"groupId": "com.example.acme",
"artifactId": "test",
"javaVersion": "1.8",
"language": "c",
"packaging": "jar",
"packageName": "com.example.acme.test",
"version": {
"id": "2.1.0.RELEASE",
"major": "2",
"minor": "2.1"
},
"dependencies": {
"id": "data-jpa web",
"values": [
"web",
"data-jpa"
],
"count": 2
},
"errorState": {
"invalid": true,
"language": true
}
}

View File

@@ -0,0 +1,27 @@
{
"generationTimestamp": 0,
"type": "not-a-type",
"groupId": "com.example.acme",
"artifactId": "test",
"javaVersion": "1.8",
"language": "java",
"packaging": "jar",
"packageName": "com.example.acme.test",
"version": {
"id": "2.1.0.RELEASE",
"major": "2",
"minor": "2.1"
},
"dependencies": {
"id": "data-jpa web",
"values": [
"web",
"data-jpa"
],
"count": 2
},
"errorState": {
"invalid": true,
"type": true
}
}

View File

@@ -0,0 +1,24 @@
{
"generationTimestamp": 0,
"type": "gradle-project",
"buildSystem": "gradle",
"groupId": "com.example.acme",
"artifactId": "test",
"javaVersion": "1.8",
"language": "java",
"packaging": "jar",
"packageName": "com.example.acme.test",
"version": {
"id": "2.1.0.RELEASE",
"major": "2",
"minor": "2.1"
},
"dependencies": {
"id": "data-jpa web",
"values": [
"web",
"data-jpa"
],
"count": 2
}
}

View File

@@ -0,0 +1,30 @@
{
"generationTimestamp": 0,
"type": "maven-project",
"buildSystem": "maven",
"groupId": "com.example.acme",
"artifactId": "project",
"javaVersion": "1.8",
"language": "java",
"packaging": "jar",
"packageName": "com.example.acme.project",
"version": {
"id": "2.1.1.RELEASE",
"major": "2",
"minor": "2.1"
},
"client": {
"id": "curl",
"version": "1.2.4",
"ip": "10.0.0.42",
"country": "BE"
},
"dependencies": {
"id": "data-jpa web",
"values": [
"web",
"data-jpa"
],
"count": 2
}
}