Skip to content

Commit 0f26bb5

Browse files
authored
feat: report runtime properties (#21)
1 parent 6692ec5 commit 0f26bb5

8 files changed

Lines changed: 116 additions & 4 deletions

File tree

codegen/internal/generator/params.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
type Params struct {
1313
SpecPath string
1414
OutputDir string
15+
ResourceDir string
1516
BasePackage string
1617
}
1718

@@ -34,6 +35,12 @@ func (p *Params) normalize() error {
3435
if p.OutputDir, err = filepath.Abs(p.OutputDir); err != nil {
3536
return fmt.Errorf("resolve output directory: %w", err)
3637
}
38+
if p.ResourceDir == "" {
39+
p.ResourceDir = filepath.Join(filepath.Dir(p.OutputDir), "resources")
40+
}
41+
if p.ResourceDir, err = filepath.Abs(p.ResourceDir); err != nil {
42+
return fmt.Errorf("resolve resource directory: %w", err)
43+
}
3744
return nil
3845
}
3946

@@ -55,6 +62,18 @@ func (p *Params) validate() error {
5562
} else if !fi.IsDir() {
5663
return fmt.Errorf("output directory %s is not a directory", p.OutputDir)
5764
}
65+
fi, err = os.Stat(p.ResourceDir)
66+
if err != nil {
67+
if errors.Is(err, os.ErrNotExist) {
68+
if err := os.MkdirAll(p.ResourceDir, 0o755); err != nil {
69+
return fmt.Errorf("create resource directory: %w", err)
70+
}
71+
} else {
72+
return fmt.Errorf("resource directory: %w", err)
73+
}
74+
} else if !fi.IsDir() {
75+
return fmt.Errorf("resource directory %s is not a directory", p.ResourceDir)
76+
}
5877
return nil
5978
}
6079

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package generator
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
)
9+
10+
func renderApiVersionResource(apiVersion string, params Params) error {
11+
apiVersion = strings.TrimSpace(apiVersion)
12+
if apiVersion == "" {
13+
return fmt.Errorf("missing api version in spec info")
14+
}
15+
targetDir := filepath.Join(params.ResourceDir, params.basePackagePath())
16+
if err := os.MkdirAll(targetDir, 0o755); err != nil {
17+
return fmt.Errorf("create api version resource directory: %w", err)
18+
}
19+
target := filepath.Join(targetDir, "api-version.txt")
20+
if err := os.WriteFile(target, []byte(apiVersion), 0o644); err != nil {
21+
return fmt.Errorf("write api version resource: %w", err)
22+
}
23+
return nil
24+
}

codegen/internal/generator/run.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ func Run(ctx context.Context, params Params) error {
3232
return err
3333
}
3434

35+
apiVersion := ""
36+
if doc.Info != nil {
37+
apiVersion = doc.Info.Version
38+
}
39+
3540
slog.Info("Generating SDK", "spec", params.SpecPath)
3641
if err := renderClients(model, params); err != nil {
3742
return err
@@ -42,6 +47,9 @@ func Run(ctx context.Context, params Params) error {
4247
if err := renderSumUpClient(model, params); err != nil {
4348
return err
4449
}
50+
if err := renderApiVersionResource(apiVersion, params); err != nil {
51+
return err
52+
}
4553

4654
return nil
4755
}

src/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ sourceSets {
2121
def emptyDirs = []
2222
main {
2323
java.srcDirs = ['main/java']
24-
resources.srcDirs = emptyDirs
24+
resources.srcDirs = ['main/resources']
2525
}
2626
test {
2727
java.srcDirs = ['test/java']

src/main/java/com/sumup/sdk/core/ApiClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ private void applyHeaders(
186186
RequestOptions requestOptions) {
187187
Map<String, String> merged = new LinkedHashMap<>();
188188
merged.put("User-Agent", SdkMetadata.userAgent());
189+
merged.putAll(SdkMetadata.runtimeHeaders());
189190
if (headerParams != null) {
190191
headerParams.forEach(
191192
(name, value) -> {

src/main/java/com/sumup/sdk/core/SdkMetadata.java

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,38 @@
33
import java.io.IOException;
44
import java.io.InputStream;
55
import java.nio.charset.StandardCharsets;
6+
import java.util.Map;
67

78
/**
89
* Provides metadata about the SDK that can be attached to outgoing requests, such as the current
910
* version and the default {@code User-Agent} header value.
1011
*/
1112
public final class SdkMetadata {
13+
private static final String API_VERSION_RESOURCE = "/com/sumup/sdk/api-version.txt";
1214
private static final String VERSION_RESOURCE = "/com/sumup/sdk/sdk-version.txt";
1315
private static final String USER_AGENT_PREFIX = "sumup-java";
16+
private static final String LANGUAGE = "java";
1417

15-
private static final String VERSION = loadVersion();
18+
private static final String API_VERSION = loadResource(API_VERSION_RESOURCE);
19+
private static final String VERSION = loadResource(VERSION_RESOURCE);
1620
private static final String USER_AGENT = USER_AGENT_PREFIX + "/v" + VERSION;
21+
private static final Map<String, String> RUNTIME_HEADERS =
22+
Map.of(
23+
"X-Sumup-Api-Version", API_VERSION,
24+
"X-Sumup-Lang", LANGUAGE,
25+
"X-Sumup-Package-Version", VERSION,
26+
"X-Sumup-OS", System.getProperty("os.name", "unknown"),
27+
"X-Sumup-Arch", runtimeArch(),
28+
"X-Sumup-Runtime", runtimeIdentifier(),
29+
"X-Sumup-Runtime-Version", Runtime.version().toString());
1730

1831
private SdkMetadata() {}
1932

33+
/** Returns the API version declared by the OpenAPI specification. */
34+
public static String apiVersion() {
35+
return API_VERSION;
36+
}
37+
2038
/** Returns the SDK version read from the generated version resource. */
2139
public static String version() {
2240
return VERSION;
@@ -27,8 +45,28 @@ public static String userAgent() {
2745
return USER_AGENT;
2846
}
2947

30-
private static String loadVersion() {
31-
try (InputStream stream = SdkMetadata.class.getResourceAsStream(VERSION_RESOURCE)) {
48+
/** Returns the runtime headers that should be sent with each request. */
49+
public static Map<String, String> runtimeHeaders() {
50+
return RUNTIME_HEADERS;
51+
}
52+
53+
static String runtimeArch() {
54+
String arch = System.getProperty("os.arch", "unknown").toLowerCase();
55+
return switch (arch) {
56+
case "amd64", "x86_64" -> "x86_64";
57+
case "x86", "i386", "i486", "i586", "i686" -> "x86";
58+
case "aarch64", "arm64" -> "arm64";
59+
case "arm", "armv7", "armv7l" -> "arm";
60+
default -> arch;
61+
};
62+
}
63+
64+
private static String runtimeIdentifier() {
65+
return LANGUAGE + Runtime.version();
66+
}
67+
68+
private static String loadResource(String path) {
69+
try (InputStream stream = SdkMetadata.class.getResourceAsStream(path)) {
3270
if (stream == null) {
3371
return "unknown";
3472
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0.0

src/test/java/com/sumup/sdk/core/ApiClientTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,27 @@ void requestOptionsCanOverrideUserAgent() {
7474
"custom/agent", httpClient.lastRequest().headers().firstValue("User-Agent").orElse(null));
7575
}
7676

77+
@Test
78+
void defaultRuntimeHeadersAreIncluded() {
79+
CapturingHttpClient httpClient = new CapturingHttpClient();
80+
ApiClient client = ApiClient.builder().httpClient(httpClient).build();
81+
82+
client.send(HttpMethod.GET, "/v1/test", null, null, null, null, null);
83+
84+
HttpHeaders headers = httpClient.lastRequest().headers();
85+
assertEquals(SdkMetadata.apiVersion(), headers.firstValue("X-Sumup-Api-Version").orElse(null));
86+
assertEquals("java", headers.firstValue("X-Sumup-Lang").orElse(null));
87+
assertEquals(SdkMetadata.version(), headers.firstValue("X-Sumup-Package-Version").orElse(null));
88+
assertEquals(
89+
System.getProperty("os.name", "unknown"), headers.firstValue("X-Sumup-OS").orElse(null));
90+
assertEquals(
91+
SdkMetadata.runtimeHeaders().get("X-Sumup-Arch"),
92+
headers.firstValue("X-Sumup-Arch").orElse(null));
93+
assertEquals("java" + Runtime.version(), headers.firstValue("X-Sumup-Runtime").orElse(null));
94+
assertEquals(
95+
Runtime.version().toString(), headers.firstValue("X-Sumup-Runtime-Version").orElse(null));
96+
}
97+
7798
@Test
7899
void requestOptionsCanOverrideTimeout() {
79100
CapturingHttpClient httpClient = new CapturingHttpClient();

0 commit comments

Comments
 (0)