From 0a1028a6023d0b5ce9fbc4b4b3cd7311a8f8a0ae Mon Sep 17 00:00:00 2001 From: Marcel Jacek Date: Mon, 17 Nov 2025 16:17:52 +0100 Subject: [PATCH] feat(core): add new `GetTraceId` function --- CHANGELOG.md | 4 ++ core/CHANGELOG.md | 3 ++ core/VERSION | 2 +- core/runtime/runtime.go | 17 ++++++++ core/runtime/runtime_test.go | 75 ++++++++++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 core/runtime/runtime_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e23087a..7cbe0857b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Release (2025-XX-YY) +- `core`: [v0.20.0](core/CHANGELOG.md#v0200) + - **New:** Added new `GetTraceId` function + ## Release (2025-11-14) - `core`: - [v0.19.0](core/CHANGELOG.md#v0190) diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 3bea787af..1ec7bffe3 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.20.0 +- **New:** Added new `GetTraceId` function + ## v0.19.0 - **New:** Added new `EnumSliceToStringSlice ` util func diff --git a/core/VERSION b/core/VERSION index 96fb87f8e..1847373e9 100644 --- a/core/VERSION +++ b/core/VERSION @@ -1 +1 @@ -v0.19.0 +v0.20.0 diff --git a/core/runtime/runtime.go b/core/runtime/runtime.go index e499d1b6a..38491049d 100644 --- a/core/runtime/runtime.go +++ b/core/runtime/runtime.go @@ -7,6 +7,10 @@ import ( "github.com/stackitcloud/stackit-sdk-go/core/config" ) +const ( + xTraceIdHeader = "x-trace-id" +) + // WithCaptureHTTPResponse adds the raw HTTP response retrieval annotation to the parent context. // The resp parameter will contain the raw HTTP response after the request has completed. func WithCaptureHTTPResponse(parent context.Context, resp **http.Response) context.Context { @@ -18,3 +22,16 @@ func WithCaptureHTTPResponse(parent context.Context, resp **http.Response) conte func WithCaptureHTTPRequest(parent context.Context, req **http.Request) context.Context { return context.WithValue(parent, config.ContextHTTPRequest, req) } + +// GetTraceId returns the X-trace-id from the last response. If no trace-id can be found, it returns an empty string. +// Prerequisite is, that WithCaptureHTTPResponse was executed before. It reads the X-trace-id header from the +// attached http response within the context. +func GetTraceId(ctx context.Context) string { + var traceId string + if resp, ok := ctx.Value(config.ContextHTTPResponse).(**http.Response); ok { + if resp != nil && *resp != nil { + traceId = (*resp).Header.Get(xTraceIdHeader) + } + } + return traceId +} diff --git a/core/runtime/runtime_test.go b/core/runtime/runtime_test.go new file mode 100644 index 000000000..f4ec0a145 --- /dev/null +++ b/core/runtime/runtime_test.go @@ -0,0 +1,75 @@ +package runtime + +import ( + "context" + "net/http" + "testing" +) + +func TestGetTraceId(t *testing.T) { + type args struct { + ctx context.Context + } + tests := []struct { + name string + args args + wantTraceId string + }{ + { + name: "with set traceId", + args: args{ + ctx: func() context.Context { + ctx := context.Background() + header := http.Header{} + header.Set(xTraceIdHeader, "my-trace-id") + httpResp := &http.Response{ + Header: header, + } + ctx = WithCaptureHTTPResponse(ctx, &httpResp) + return ctx + }(), + }, + wantTraceId: "my-trace-id", + }, + { + name: "without traceId header", + args: args{ + ctx: func() context.Context { + ctx := context.Background() + httpResp := &http.Response{ + Header: http.Header{}, + } + ctx = WithCaptureHTTPResponse(ctx, &httpResp) + return ctx + }(), + }, + wantTraceId: "", + }, + { + name: "with empty response", + args: args{ + ctx: func() context.Context { + ctx := context.Background() + var httpResp *http.Response + ctx = WithCaptureHTTPResponse(ctx, &httpResp) + return ctx + }(), + }, + wantTraceId: "", + }, + { + name: "without response in context", + args: args{ + ctx: context.Background(), + }, + wantTraceId: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotTraceId := GetTraceId(tt.args.ctx); gotTraceId != tt.wantTraceId { + t.Errorf("GetTraceId() = %v, want %v", gotTraceId, tt.wantTraceId) + } + }) + } +}