Skip to content

Commit 75578a8

Browse files
Add more docs and tests
1 parent 11fe904 commit 75578a8

10 files changed

Lines changed: 404 additions & 252 deletions

File tree

gradle/jacoco.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies {
1313
jacocoAggregation project(':temporal-spring-boot-starter')
1414
jacocoAggregation project(':temporal-test-server')
1515
jacocoAggregation project(':temporal-testing')
16+
jacocoAggregation project(':temporal-envconfig')
1617
}
1718

1819
def jacocoExclusions = [

temporal-bom/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ dependencies {
1616
api project(':temporal-spring-boot-starter')
1717
api project(':temporal-test-server')
1818
api project(':temporal-testing')
19+
api project(':temporal-envconfig')
1920
}
2021
}

temporal-envconfig/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ dependencies {
44
// this module shouldn't carry temporal-sdk with it, especially for situations when users may be using a shaded artifact
55
compileOnly project(':temporal-sdk')
66

7-
implementation 'org.tomlj:tomlj:1.1.1'
87
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.15.4'
98
testImplementation project(":temporal-testing")
109
testImplementation "junit:junit:${junitVersion}"
Lines changed: 19 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
package io.temporal.envconfig;
22

3-
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4-
import com.fasterxml.jackson.annotation.JsonInclude;
5-
import com.fasterxml.jackson.annotation.JsonProperty;
63
import com.fasterxml.jackson.databind.ObjectReader;
74
import com.fasterxml.jackson.dataformat.toml.TomlMapper;
8-
import io.grpc.Metadata;
95
import io.temporal.common.Experimental;
106
import java.io.*;
11-
import java.util.Collections;
12-
import java.util.HashMap;
137
import java.util.Map;
14-
import javax.annotation.Nullable;
158

9+
/** ClientConfig represents a client config file. */
1610
@Experimental
1711
public class ClientConfig {
1812

13+
/** Get the default config file path: $HOME/.config/temporal/temporal.toml */
1914
private static String getDefaultConfigFilePath() {
2015
String userDir = System.getProperty("user.home");
2116
if (userDir == null || userDir.isEmpty()) {
@@ -41,21 +36,29 @@ public static ClientConfig load() throws IOException {
4136
* <p>This does not apply environment variable overrides to the profiles, it only uses an
4237
* environment variable to find the default config file path (TEMPORAL_CONFIG_FILE). To get a
4338
* single profile with environment variables applied, use {@link ClientConfigProfile#load}.
39+
*
40+
* @param options options to control loading the config
41+
* @throws IOException if the config file cannot be read or parsed
4442
*/
4543
public static ClientConfig load(LoadClientConfigOptions options) throws IOException {
46-
ObjectReader reader = new TomlMapper().readerFor(TomlClientConfig.class);
44+
ObjectReader reader = new TomlMapper().readerFor(ClientConfigToml.TomlClientConfig.class);
4745
if (options.isStrictConfigFile()) {
4846
reader =
4947
reader.withFeatures(
5048
com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
49+
} else {
50+
reader =
51+
reader.withoutFeatures(
52+
com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
5153
}
54+
5255
if (options.getConfigFileData() != null && options.getConfigFileData().length > 0) {
5356
if (options.getConfigFilePath() != null && !options.getConfigFilePath().isEmpty()) {
5457
throw new IllegalArgumentException(
5558
"Cannot have both ConfigFileData and ConfigFilePath set");
5659
}
57-
TomlClientConfig result = reader.readValue(options.getConfigFileData());
58-
return new ClientConfig(result);
60+
ClientConfigToml.TomlClientConfig result = reader.readValue(options.getConfigFileData());
61+
return new ClientConfig(ClientConfigToml.getClientProfiles(result));
5962
} else {
6063
// Get file name which is either set value, env var, or default path
6164
String file = options.getConfigFilePath();
@@ -71,172 +74,19 @@ public static ClientConfig load(LoadClientConfigOptions options) throws IOExcept
7174
if (file == null || file.isEmpty()) {
7275
file = getDefaultConfigFilePath();
7376
}
74-
TomlClientConfig result = reader.readValue(file);
75-
return new ClientConfig(result);
77+
ClientConfigToml.TomlClientConfig result = reader.readValue(new File(file));
78+
return new ClientConfig(ClientConfigToml.getClientProfiles(result));
7679
}
7780
}
7881

79-
private final Map<String, ClientConfigProfile> profiles;
80-
81-
private ClientConfig() {
82-
profiles = Collections.emptyMap();
83-
}
84-
85-
private String normalizeGrpcMetaKey(String key) {
86-
return key.toLowerCase().replace('_', '-');
87-
}
88-
89-
private ClientConfig(TomlClientConfig clientConfig) {
90-
profiles = new HashMap<>(clientConfig.profiles.size());
91-
for (Map.Entry<String, TomlClientConfigProfile> entry : clientConfig.profiles.entrySet()) {
92-
String profileName = entry.getKey();
93-
TomlClientConfigProfile tomlProfile = entry.getValue();
94-
ClientConfigTLS tls = getClientConfigTLS(tomlProfile);
95-
Metadata metadata = null;
96-
if (tomlProfile.grpcMeta != null) {
97-
for (Map.Entry<String, String> metaEntry : tomlProfile.grpcMeta.entrySet()) {
98-
if (metadata == null) {
99-
metadata = new Metadata();
100-
}
101-
Metadata.Key<String> key =
102-
Metadata.Key.of(
103-
normalizeGrpcMetaKey(metaEntry.getKey()), Metadata.ASCII_STRING_MARSHALLER);
104-
metadata.put(key, metaEntry.getValue());
105-
}
106-
}
107-
ClientConfigProfile profile =
108-
ClientConfigProfile.newBuilder()
109-
.setAddress(tomlProfile.address)
110-
.setNamespace(tomlProfile.namespace)
111-
.setApiKey(tomlProfile.apiKey)
112-
.setMetadata(metadata)
113-
.setTls(tls)
114-
.build();
115-
profiles.put(profileName, profile);
116-
}
82+
public ClientConfig(Map<String, ClientConfigProfile> profiles) {
83+
this.profiles = profiles;
11784
}
11885

119-
@Nullable
120-
private static ClientConfigTLS getClientConfigTLS(TomlClientConfigProfile tomlProfile) {
121-
ClientConfigTLS tls = null;
122-
if (tomlProfile.tls != null) {
123-
tls =
124-
new ClientConfigTLS(
125-
tomlProfile.tls.disabled,
126-
tomlProfile.tls.clientCertPath,
127-
tomlProfile.tls.clientCertData.getBytes(),
128-
tomlProfile.tls.clientKeyPath,
129-
tomlProfile.tls.clientKeyData.getBytes(),
130-
tomlProfile.tls.serverCACertPath,
131-
tomlProfile.tls.serverCACertData.getBytes(),
132-
tomlProfile.tls.serverName,
133-
tomlProfile.tls.disableHostVerification);
134-
}
135-
return tls;
136-
}
86+
private final Map<String, ClientConfigProfile> profiles;
13787

88+
/** All profiles loaded from the config file, may be empty but never null. */
13889
public Map<String, ClientConfigProfile> getProfiles() {
13990
return profiles;
14091
}
141-
142-
@JsonIgnoreProperties(ignoreUnknown = true)
143-
@JsonInclude(JsonInclude.Include.NON_EMPTY)
144-
static class TomlClientConfig {
145-
@JsonProperty("profile")
146-
public Map<String, TomlClientConfigProfile> profiles;
147-
148-
protected TomlClientConfig() {}
149-
150-
TomlClientConfig(Map<String, TomlClientConfigProfile> profiles) {
151-
this.profiles = profiles;
152-
}
153-
}
154-
155-
@JsonIgnoreProperties(ignoreUnknown = true)
156-
@JsonInclude(JsonInclude.Include.NON_EMPTY)
157-
static class TomlClientConfigProfile {
158-
@JsonProperty("address")
159-
public String address;
160-
161-
@JsonProperty("namespace")
162-
public String namespace;
163-
164-
@JsonProperty("api_key")
165-
public String apiKey;
166-
167-
@JsonProperty("tls")
168-
public TomlClientConfigTLS tls;
169-
170-
@JsonProperty("grpc_meta")
171-
public Map<String, String> grpcMeta;
172-
173-
protected TomlClientConfigProfile() {}
174-
175-
TomlClientConfigProfile(
176-
String address,
177-
String namespace,
178-
String apiKey,
179-
TomlClientConfigTLS tls,
180-
Map<String, String> grpcMeta) {
181-
this.address = address;
182-
this.namespace = namespace;
183-
this.apiKey = apiKey;
184-
this.tls = tls;
185-
this.grpcMeta = grpcMeta;
186-
}
187-
}
188-
189-
@JsonIgnoreProperties(ignoreUnknown = true)
190-
@JsonInclude(JsonInclude.Include.NON_EMPTY)
191-
static class TomlClientConfigTLS {
192-
@JsonProperty("disabled")
193-
public boolean disabled;
194-
195-
@JsonProperty("client_cert_path")
196-
public String clientCertPath;
197-
198-
@JsonProperty("client_cert_data")
199-
public String clientCertData;
200-
201-
@JsonProperty("client_key_path")
202-
public String clientKeyPath;
203-
204-
@JsonProperty("client_key_data")
205-
public String clientKeyData;
206-
207-
@JsonProperty("server_ca_cert_path")
208-
public String serverCACertPath;
209-
210-
@JsonProperty("server_ca_cert_data")
211-
public String serverCACertData;
212-
213-
@JsonProperty("server_name")
214-
public String serverName;
215-
216-
@JsonProperty("disable_host_verification")
217-
public boolean disableHostVerification;
218-
219-
protected TomlClientConfigTLS() {}
220-
221-
TomlClientConfigTLS(
222-
boolean disabled,
223-
String clientCertPath,
224-
String clientCertData,
225-
String clientKeyPath,
226-
String clientKeyData,
227-
String serverCACertPath,
228-
String serverCACertData,
229-
String serverName,
230-
boolean disableHostVerification) {
231-
this.disabled = disabled;
232-
this.clientCertPath = clientCertPath;
233-
this.clientCertData = clientCertData;
234-
this.clientKeyPath = clientKeyPath;
235-
this.clientKeyData = clientKeyData;
236-
this.serverCACertPath = serverCACertPath;
237-
this.serverCACertData = serverCACertData;
238-
this.serverName = serverName;
239-
this.disableHostVerification = disableHostVerification;
240-
}
241-
}
24292
}

temporal-envconfig/src/main/java/io/temporal/envconfig/ClientConfigProfile.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,17 @@
1515
/** ClientConfigProfile is profile-level configuration for a client. */
1616
@Experimental
1717
public class ClientConfigProfile {
18+
/** Creates a new builder to build a ClientConfigProfile. */
1819
public static Builder newBuilder() {
1920
return new Builder();
2021
}
2122

23+
/**
24+
* Creates a new builder to build a ClientConfigProfile based on an existing profile.
25+
*
26+
* @param profile the existing profile to base the builder on
27+
* @return a new Builder instance
28+
*/
2229
public static Builder newBuilder(ClientConfigProfile profile) {
2330
return new Builder(profile);
2431
}
@@ -46,6 +53,10 @@ public Builder toBuilder() {
4653
return new Builder(this);
4754
}
4855

56+
/**
57+
* Converts this profile to WorkflowServiceStubsOptions. Note that not all fields are converted,
58+
* only those relevant to service stubs.
59+
*/
4960
public WorkflowServiceStubsOptions toWorkflowServiceStubsOptions() {
5061
WorkflowServiceStubsOptions.Builder builder = WorkflowServiceStubsOptions.newBuilder();
5162
if (this.address != null && !this.address.isEmpty()) {
@@ -100,11 +111,18 @@ public WorkflowServiceStubsOptions toWorkflowServiceStubsOptions() {
100111
if (this.tls.getServerName() != null && !this.tls.getServerName().isEmpty()) {
101112
builder.setChannelInitializer(c -> c.overrideAuthority(this.tls.getServerName()));
102113
}
114+
} else if (this.apiKey != null && !this.apiKey.isEmpty()) {
115+
// If API key is set, TLS is required, so enable it with defaults
116+
builder.setEnableHttps(true);
103117
}
104118

105119
return builder.build();
106120
}
107121

122+
/**
123+
* Converts this profile to WorkflowClientOptions. Note that not all fields are converted, only
124+
* those relevant to client options.
125+
*/
108126
public WorkflowClientOptions toWorkflowClientOptions() {
109127
WorkflowClientOptions.Builder builder = WorkflowClientOptions.newBuilder();
110128
if (this.namespace != null && !this.namespace.isEmpty()) {
@@ -156,8 +174,7 @@ public static ClientConfigProfile load(LoadClientConfigProfileOptions options)
156174
}
157175
// If env is enabled, apply it on top of an empty profile
158176
if (!options.isDisableEnv()) {
159-
Map<String, String> overrideEnvVars = null;
160-
clientConfigProfile.applyEnvOverrides(overrideEnvVars);
177+
clientConfigProfile.applyEnvOverrides(options.getEnvOverrides());
161178
}
162179
return clientConfigProfile;
163180
}
@@ -328,26 +345,42 @@ public Builder(ClientConfigProfile profile) {
328345
this.tls = profile.tls;
329346
}
330347

348+
/**
349+
* Sets the namespace for the client. This is optional; if not set, the default namespace will
350+
* be used.
351+
*/
331352
public Builder setNamespace(String namespace) {
332353
this.namespace = namespace;
333354
return this;
334355
}
335356

357+
/**
358+
* Sets the address of the Temporal service endpoint. This is optional; if not set, the default
359+
* address will be used.
360+
*/
336361
public Builder setAddress(String address) {
337362
this.address = address;
338363
return this;
339364
}
340365

366+
/** Sets the API key for the client. This is optional; if not set, no API key will be used. */
341367
public Builder setApiKey(String apiKey) {
342368
this.apiKey = apiKey;
343369
return this;
344370
}
345371

372+
/**
373+
* Sets the gRPC metadata to be sent with each request. This is optional; if not set, no
374+
* metadata will be sent.
375+
*/
346376
public Builder setMetadata(Metadata metadata) {
347377
this.metadata = metadata;
348378
return this;
349379
}
350380

381+
/**
382+
* Sets the TLS configuration for the client. This is optional; if not set, no TLS will be used.
383+
*/
351384
public Builder setTls(ClientConfigTLS tls) {
352385
this.tls = tls;
353386
return this;

0 commit comments

Comments
 (0)