Skip to content

Commit 295c6c5

Browse files
authored
Configuration from file or environment variables (#106)
* VisualRegressionTrackerConfigBuilder extended to support config file and env variables
1 parent 31f52b7 commit 295c6c5

File tree

5 files changed

+296
-16
lines changed

5 files changed

+296
-16
lines changed

build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dependencies {
3232
testImplementation 'commons-io:commons-io:2.11.0'
3333
testImplementation 'org.mockito:mockito-core:4.4.0'
3434
testImplementation 'com.squareup.okhttp3:mockwebserver:4.9.3'
35+
testImplementation 'uk.org.webcompere:system-stubs-testng:2.1.3'
3536
}
3637

3738
test {

src/main/java/io/visual_regression_tracker/sdk_java/VisualRegressionTracker.java

+13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.visual_regression_tracker.sdk_java.response.TestRunResponse;
88
import lombok.extern.slf4j.Slf4j;
99

10+
import java.io.File;
1011
import java.io.IOException;
1112
import java.net.URI;
1213
import java.net.http.HttpClient;
@@ -24,6 +25,7 @@ enum METHOD {
2425
public class VisualRegressionTracker {
2526

2627
private static final String TRACKER_NOT_STARTED = "Visual Regression Tracker has not been started";
28+
private static final String CONFIG_FILE_NAME = "vrt.json";
2729
protected static final String API_KEY_HEADER = "apiKey";
2830
protected static final String PROJECT_HEADER = "project";
2931
protected Gson gson;
@@ -32,6 +34,17 @@ public class VisualRegressionTracker {
3234
protected String buildId;
3335
protected String projectId;
3436

37+
public VisualRegressionTracker() {
38+
VisualRegressionTrackerConfig.VisualRegressionTrackerConfigBuilder configBuilder = VisualRegressionTrackerConfig.builder();
39+
File configFile = new File(CONFIG_FILE_NAME);
40+
if (configFile.exists()) {
41+
configBuilder.configFile(configFile);
42+
}
43+
configuration = configBuilder.build();
44+
paths = new PathProvider(configuration.getApiUrl());
45+
gson = new Gson();
46+
}
47+
3548
public VisualRegressionTracker(VisualRegressionTrackerConfig trackerConfig) {
3649
configuration = trackerConfig;
3750
paths = new PathProvider(trackerConfig.getApiUrl());
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
package io.visual_regression_tracker.sdk_java;
22

3-
import lombok.AllArgsConstructor;
4-
import lombok.Builder;
5-
import lombok.Data;
6-
import lombok.NonNull;
7-
import lombok.RequiredArgsConstructor;
8-
9-
@Data
10-
@Builder
3+
import com.google.gson.Gson;
4+
import com.google.gson.reflect.TypeToken;
5+
import lombok.*;
6+
import lombok.experimental.Accessors;
7+
import lombok.extern.slf4j.Slf4j;
8+
9+
import java.io.File;
10+
import java.io.IOException;
11+
import java.lang.reflect.Field;
12+
import java.lang.reflect.Type;
13+
import java.nio.charset.StandardCharsets;
14+
import java.nio.file.Files;
15+
import java.util.Collections;
16+
import java.util.Map;
17+
import java.util.function.Function;
18+
19+
@Data()
1120
@RequiredArgsConstructor
1221
@AllArgsConstructor
22+
@Accessors(chain = true)
23+
@Slf4j
1324
public class VisualRegressionTrackerConfig {
1425

1526
@NonNull
@@ -18,12 +29,145 @@ public class VisualRegressionTrackerConfig {
1829
private final String apiKey;
1930
@NonNull
2031
private final String project;
21-
@Builder.Default
22-
private String branchName = null;
23-
@Builder.Default
24-
private String ciBuildId = null;
25-
@Builder.Default
26-
private Boolean enableSoftAssert = false;
27-
@Builder.Default
28-
private int httpTimeoutInSeconds = 10;
32+
33+
private String branchName;
34+
private String ciBuildId;
35+
private Boolean enableSoftAssert;
36+
private int httpTimeoutInSeconds;
37+
38+
public static VisualRegressionTrackerConfigBuilder builder() {
39+
return new VisualRegressionTrackerConfigBuilder();
40+
}
41+
42+
public static class VisualRegressionTrackerConfigBuilder {
43+
private String apiUrl;
44+
private String apiKey;
45+
private String project;
46+
47+
private String branchName;
48+
private String ciBuildId;
49+
private Boolean enableSoftAssert;
50+
private Integer httpTimeoutInSeconds;
51+
52+
private File configFile;
53+
54+
private static final String VRT_ENV_VARIABLE_PREFIX = "VRT_";
55+
private static final boolean DEFAULT_SOFT_ASSERTION_STATE = false;
56+
private static final int DEFAULT_HTTP_TIMEOUT_SECONDS = 10;
57+
58+
public VisualRegressionTrackerConfigBuilder apiUrl(String apiUrl) {
59+
this.apiUrl = apiUrl;
60+
return this;
61+
}
62+
63+
public VisualRegressionTrackerConfigBuilder apiKey(String apiKey) {
64+
this.apiKey = apiKey;
65+
return this;
66+
}
67+
68+
public VisualRegressionTrackerConfigBuilder project(String project) {
69+
this.project = project;
70+
return this;
71+
}
72+
73+
public VisualRegressionTrackerConfigBuilder branchName(String branchName) {
74+
this.branchName = branchName;
75+
return this;
76+
}
77+
78+
public VisualRegressionTrackerConfigBuilder ciBuildId(String ciBuildId) {
79+
this.ciBuildId = ciBuildId;
80+
return this;
81+
}
82+
83+
public VisualRegressionTrackerConfigBuilder enableSoftAssert(Boolean enableSoftAssert) {
84+
this.enableSoftAssert = enableSoftAssert;
85+
return this;
86+
}
87+
88+
public VisualRegressionTrackerConfigBuilder httpTimeoutInSeconds(int httpTimeoutInSeconds) {
89+
this.httpTimeoutInSeconds = httpTimeoutInSeconds;
90+
return this;
91+
}
92+
93+
public VisualRegressionTrackerConfigBuilder configFile(File configFile) {
94+
this.configFile = configFile;
95+
return this;
96+
}
97+
98+
public VisualRegressionTrackerConfig build() {
99+
Map<String, Object> configFromFile = Collections.emptyMap();
100+
if (configFile != null) {
101+
configFromFile = readConfigFromFile(configFile);
102+
}
103+
104+
String actualApiUrl = resolve("apiUrl", configFromFile);
105+
String actualApiKey = resolve("apiKey", configFromFile);
106+
String actualProject = resolve("project", configFromFile);
107+
108+
VisualRegressionTrackerConfig config = new VisualRegressionTrackerConfig(actualApiUrl, actualApiKey, actualProject);
109+
config.setCiBuildId(resolve("ciBuildId", configFromFile));
110+
config.setBranchName(resolve("branchName", configFromFile));
111+
112+
Boolean actualEnableSoftAssert = resolve("enableSoftAssert", configFromFile);
113+
config.setEnableSoftAssert(actualEnableSoftAssert == null ? DEFAULT_SOFT_ASSERTION_STATE : actualEnableSoftAssert);
114+
115+
Integer actualHttpTimeoutInSeconds = resolve("httpTimeoutInSeconds", configFromFile);
116+
config.setHttpTimeoutInSeconds(actualHttpTimeoutInSeconds == null ? DEFAULT_HTTP_TIMEOUT_SECONDS : actualHttpTimeoutInSeconds);
117+
118+
return config;
119+
}
120+
121+
private Map<String, Object> readConfigFromFile(File configFile) {
122+
if (!configFile.exists()) {
123+
throw new IllegalArgumentException("File " + configFile + " doesn't exist");
124+
}
125+
126+
String fileContent;
127+
try {
128+
fileContent = Files.readString(configFile.toPath(), StandardCharsets.UTF_8);
129+
} catch (IOException e) {
130+
throw new IllegalArgumentException("Can't read content of provided config file", e);
131+
}
132+
Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
133+
return new Gson().fromJson(fileContent, mapType);
134+
}
135+
136+
@SneakyThrows
137+
private <T> T resolve(String propertyName, Map<String, Object> configurationFromFile) {
138+
// 1. check if it was initialized explicitly in builder
139+
// 2. check if env variable exists
140+
// 3. try to read from file as last resort
141+
Field field = this.getClass().getDeclaredField(propertyName);
142+
Object propertyValue = field.get(this);
143+
if (propertyValue != null) {
144+
return (T) propertyValue;
145+
}
146+
147+
String environmentVariableName = VRT_ENV_VARIABLE_PREFIX + propertyName.toUpperCase();
148+
propertyValue = System.getenv(environmentVariableName);
149+
if (propertyValue != null) {
150+
log.debug("Value of '{}' resolved from environment variable {}", propertyName, environmentVariableName);
151+
Function<String, ?> parser = findParser(field.getType());
152+
return (T) parser.apply((String)propertyValue);
153+
}
154+
155+
propertyValue = configurationFromFile.get(propertyName);
156+
if (propertyValue != null) {
157+
log.debug("Value of '{}' resolved from config file", propertyName);
158+
}
159+
return propertyValue == null ? null : (T) propertyValue;
160+
}
161+
162+
private Function<String, ?> findParser(Class<?> cls) {
163+
if (cls.equals(Boolean.class)) {
164+
return Boolean::parseBoolean;
165+
}
166+
if (cls.equals(Integer.class)) {
167+
return Integer::parseInt;
168+
}
169+
return String::valueOf;
170+
}
171+
}
172+
29173
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package io.visual_regression_tracker.sdk_java;
2+
3+
import org.testng.annotations.Test;
4+
import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
5+
6+
import java.io.File;
7+
8+
import static org.hamcrest.CoreMatchers.is;
9+
import static org.hamcrest.MatcherAssert.assertThat;
10+
11+
public class VisualRegressionTrackerConfigTest {
12+
13+
@Test(expectedExceptions = NullPointerException.class,
14+
expectedExceptionsMessageRegExp = "apiKey is marked non-null but is null")
15+
public void shouldThrowExceptionIfApiKeyMissed() {
16+
VisualRegressionTrackerConfig
17+
.builder()
18+
.apiUrl("")
19+
.project("")
20+
.build();
21+
}
22+
23+
@Test(expectedExceptions = NullPointerException.class,
24+
expectedExceptionsMessageRegExp = "apiUrl is marked non-null but is null")
25+
public void shouldThrowExceptionIfApiUrlMissed() {
26+
VisualRegressionTrackerConfig
27+
.builder()
28+
.apiKey("")
29+
.project("")
30+
.build();
31+
}
32+
33+
@Test(expectedExceptions = NullPointerException.class,
34+
expectedExceptionsMessageRegExp = "project is marked non-null but is null")
35+
public void shouldThrowExceptionIfProjectMissed() {
36+
VisualRegressionTrackerConfig
37+
.builder()
38+
.apiKey("")
39+
.apiUrl("")
40+
.build();
41+
}
42+
43+
@Test
44+
public void shouldBeCreatedFromConfigFile() {
45+
File configFile = new File("src/test/resources/vrt_config_example.json");
46+
47+
VisualRegressionTrackerConfig config = VisualRegressionTrackerConfig.builder()
48+
.configFile(configFile)
49+
.build();
50+
51+
assertThat(config.getApiKey(), is("SECRET"));
52+
assertThat(config.getApiUrl(), is("http://162.243.161.172:4200"));
53+
assertThat(config.getProject(), is("VRT"));
54+
assertThat(config.getBranchName(), is("master"));
55+
assertThat(config.getEnableSoftAssert(), is(false));
56+
assertThat(config.getCiBuildId(), is("SOME_UNIQUE_ID"));
57+
}
58+
59+
@Test
60+
public void shouldBeCreatedFromEnvironment() throws Exception {
61+
EnvironmentVariables environmentVariables = new EnvironmentVariables("VRT_APIKEY", "SECRET")
62+
.set("VRT_APIURL", "http://162.243.161.172:4200")
63+
.set("VRT_PROJECT", "VRT")
64+
.set("VRT_BRANCHNAME", "master")
65+
.set("VRT_ENABLESOFTASSERT", "false")
66+
.set("VRT_CIBUILDID", "SOME_UNIQUE_ID");
67+
68+
VisualRegressionTrackerConfig config = environmentVariables.execute(() ->
69+
VisualRegressionTrackerConfig.builder()
70+
.build()
71+
);
72+
73+
assertThat(config.getApiKey(), is("SECRET"));
74+
assertThat(config.getApiUrl(), is("http://162.243.161.172:4200"));
75+
assertThat(config.getProject(), is("VRT"));
76+
assertThat(config.getBranchName(), is("master"));
77+
assertThat(config.getEnableSoftAssert(), is(false));
78+
assertThat(config.getCiBuildId(), is("SOME_UNIQUE_ID"));
79+
}
80+
81+
@Test
82+
public void shouldResolveFinalValuesInTheRightOrder() throws Exception {
83+
EnvironmentVariables environmentVariables = new EnvironmentVariables("VRT_APIKEY", "ENV_SECRET")
84+
.set("VRT_APIURL", "http://162.243.161.172:4201");
85+
File configFile = new File("src/test/resources/vrt_config_example.json");
86+
87+
VisualRegressionTrackerConfig config = environmentVariables.execute(() ->
88+
VisualRegressionTrackerConfig.builder()
89+
.apiUrl("http://localhost:4200")
90+
.configFile(configFile)
91+
.build()
92+
);
93+
94+
assertThat(config.getApiKey(), is("ENV_SECRET"));
95+
assertThat(config.getApiUrl(), is("http://localhost:4200"));
96+
assertThat(config.getProject(), is("VRT"));
97+
assertThat(config.getBranchName(), is("master"));
98+
assertThat(config.getEnableSoftAssert(), is(false));
99+
assertThat(config.getCiBuildId(), is("SOME_UNIQUE_ID"));
100+
}
101+
102+
103+
@Test
104+
public void shouldUseDefaultValuesIfNotProvided() {
105+
VisualRegressionTrackerConfig config = VisualRegressionTrackerConfig.builder()
106+
.apiUrl("http://localhost:4200")
107+
.apiKey("KEY")
108+
.project("PROJECT")
109+
.build();
110+
111+
assertThat(config.getEnableSoftAssert(), is(false));
112+
assertThat(config.getHttpTimeoutInSeconds(), is(10));
113+
}
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"apiUrl": "http://162.243.161.172:4200",
3+
"project": "VRT",
4+
"apiKey": "SECRET",
5+
"branchName": "master",
6+
"enableSoftAssert": false,
7+
"ciBuildId": "SOME_UNIQUE_ID"
8+
}

0 commit comments

Comments
 (0)