Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
3 changes: 3 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## v0.20.0
- **New:** Added new `GetTraceId` function

## v0.19.0
- **New:** Added new `EnumSliceToStringSlice ` util func

Expand Down
2 changes: 1 addition & 1 deletion core/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.19.0
v0.20.0
17 changes: 17 additions & 0 deletions core/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
75 changes: 75 additions & 0 deletions core/runtime/runtime_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}
Loading