diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index c18ebd5af96..7770a2ec342 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -5996,7 +5996,7 @@ func (e mockUpdateBidRequestHook) HandleBidderRequestHook(_ context.Context, mct
}, hookstage.MutationUpdate, "bidRequest", "site.domain",
)
- mctx.ModuleContext = map[string]interface{}{"some-ctx": "some-ctx"}
+ mctx.ModuleContext.Set("some-ctx", "some-ctx")
return hookstage.HookResult[hookstage.BidderRequestPayload]{ChangeSet: c, ModuleContext: mctx.ModuleContext}, nil
}
diff --git a/hooks/hookexecution/context.go b/hooks/hookexecution/context.go
index fee4e7385a7..d0d8073ddaa 100644
--- a/hooks/hookexecution/context.go
+++ b/hooks/hookexecution/context.go
@@ -43,24 +43,27 @@ func (ctx executionContext) getModuleContext(moduleName string) hookstage.Module
// moduleContexts preserves data the module wants to pass to itself from earlier stages to later stages.
type moduleContexts struct {
sync.RWMutex
- ctxs map[string]hookstage.ModuleContext // format: {"module_name": hookstage.ModuleContext}
+ ctxs map[string]*hookstage.ModuleContext // format: {"module_name": hookstage.ModuleContext}
}
-func (mc *moduleContexts) put(moduleName string, mCtx hookstage.ModuleContext) {
+func (mc *moduleContexts) put(moduleName string, mCtx *hookstage.ModuleContext) {
+ if mc == nil {
+ return
+ }
mc.Lock()
defer mc.Unlock()
- newCtx := mCtx
- if existingCtx, ok := mc.ctxs[moduleName]; ok && existingCtx != nil {
- for k, v := range mCtx {
- existingCtx[k] = v
- }
- newCtx = existingCtx
+ existingCtx, ok := mc.ctxs[moduleName]
+ if !ok {
+ mc.ctxs[moduleName] = mCtx
+ return
}
- mc.ctxs[moduleName] = newCtx
+
+ // Add new data to existing context
+ existingCtx.SetAll(mCtx.GetAll())
}
-func (mc *moduleContexts) get(moduleName string) (hookstage.ModuleContext, bool) {
+func (mc *moduleContexts) get(moduleName string) (*hookstage.ModuleContext, bool) {
mc.RLock()
defer mc.RUnlock()
mCtx, ok := mc.ctxs[moduleName]
@@ -72,4 +75,4 @@ type stageModuleContext struct {
groupCtx []groupModuleContext
}
-type groupModuleContext map[string]hookstage.ModuleContext
+type groupModuleContext map[string]*hookstage.ModuleContext
diff --git a/hooks/hookexecution/executor.go b/hooks/hookexecution/executor.go
index c33cfcdda57..eb66caf64ab 100644
--- a/hooks/hookexecution/executor.go
+++ b/hooks/hookexecution/executor.go
@@ -68,7 +68,7 @@ func NewHookExecutor(builder hooks.ExecutionPlanBuilder, endpoint string, me met
endpoint: endpoint,
planBuilder: builder,
stageOutcomes: []StageOutcome{},
- moduleContexts: &moduleContexts{ctxs: make(map[string]hookstage.ModuleContext)},
+ moduleContexts: &moduleContexts{ctxs: make(map[string]*hookstage.ModuleContext)},
metricEngine: me,
}
}
diff --git a/hooks/hookexecution/executor_test.go b/hooks/hookexecution/executor_test.go
index dd309dffa9a..3dd0e76c02f 100644
--- a/hooks/hookexecution/executor_test.go
+++ b/hooks/hookexecution/executor_test.go
@@ -61,7 +61,7 @@ func TestExecuteEntrypointStage(t *testing.T) {
const body string = `{"name": "John", "last_name": "Doe"}`
const urlString string = "https://prebid.com/openrtb2/auction"
- foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}}
+ foobarModuleCtx := &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}}
testCases := []struct {
description string
@@ -84,7 +84,7 @@ func TestExecuteEntrypointStage(t *testing.T) {
expectedHeader: http.Header{},
expectedQuery: url.Values{},
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{}},
expectedStageOutcomes: []StageOutcome{},
},
{
@@ -299,9 +299,18 @@ func TestExecuteEntrypointStage(t *testing.T) {
expectedHeader: http.Header{},
expectedQuery: url.Values{},
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {"entrypoint-ctx-1": "some-ctx-1", "entrypoint-ctx-3": "some-ctx-3"},
- "module-2": {"entrypoint-ctx-2": "some-ctx-2"},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-1", "some-ctx-1")
+ mc.Set("entrypoint-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}},
expectedStageOutcomes: []StageOutcome{
{
@@ -418,7 +427,7 @@ func TestExecuteRawAuctionStage(t *testing.T) {
const bodyUpdated string = `{"last_name": "Doe", "foo": "bar"}`
const urlString string = "https://prebid.com/openrtb2/auction"
- foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}}
+ foobarModuleCtx := &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}}
testCases := []struct {
description string
@@ -437,7 +446,7 @@ func TestExecuteRawAuctionStage(t *testing.T) {
givenPlanBuilder: hooks.EmptyPlanBuilder{},
expectedBody: body,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{}},
expectedStageOutcomes: []StageOutcome{},
},
{
@@ -613,9 +622,18 @@ func TestExecuteRawAuctionStage(t *testing.T) {
givenPlanBuilder: TestWithModuleContextsPlanBuilder{},
expectedBody: body,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {"raw-auction-ctx-1": "some-ctx-1", "raw-auction-ctx-3": "some-ctx-3"},
- "module-2": {"raw-auction-ctx-2": "some-ctx-2"},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("raw-auction-ctx-1", "some-ctx-1")
+ mc.Set("raw-auction-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("raw-auction-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}},
expectedStageOutcomes: []StageOutcome{
{
@@ -691,7 +709,7 @@ func TestExecuteRawAuctionStage(t *testing.T) {
}
func TestExecuteProcessedAuctionStage(t *testing.T) {
- foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}}
+ foobarModuleCtx := &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}}
req := openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id"}}
reqUpdated := openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id", Yob: 2000, Consent: "true"}}
@@ -710,7 +728,7 @@ func TestExecuteProcessedAuctionStage(t *testing.T) {
givenRequest: openrtb_ext.RequestWrapper{BidRequest: &req},
expectedRequest: req,
expectedErr: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{}},
expectedStageOutcomes: []StageOutcome{},
},
{
@@ -831,9 +849,18 @@ func TestExecuteProcessedAuctionStage(t *testing.T) {
givenRequest: openrtb_ext.RequestWrapper{BidRequest: &req},
expectedRequest: req,
expectedErr: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {"processed-auction-ctx-1": "some-ctx-1", "processed-auction-ctx-3": "some-ctx-3"},
- "module-2": {"processed-auction-ctx-2": "some-ctx-2"},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("processed-auction-ctx-1", "some-ctx-1")
+ mc.Set("processed-auction-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("processed-auction-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}},
expectedStageOutcomes: []StageOutcome{
{
@@ -910,7 +937,7 @@ func TestExecuteProcessedAuctionStage(t *testing.T) {
func TestExecuteBidderRequestStage(t *testing.T) {
bidderName := "the-bidder"
- foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}}
+ foobarModuleCtx := &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}}
expectedBidderRequest := &openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id"}}
expectedUpdatedBidderRequest := &openrtb2.BidRequest{
@@ -938,7 +965,7 @@ func TestExecuteBidderRequestStage(t *testing.T) {
givenPlanBuilder: hooks.EmptyPlanBuilder{},
expectedBidderRequest: expectedBidderRequest,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{}},
expectedStageOutcomes: []StageOutcome{},
},
{
@@ -1106,9 +1133,17 @@ func TestExecuteBidderRequestStage(t *testing.T) {
givenPlanBuilder: TestWithModuleContextsPlanBuilder{},
expectedBidderRequest: expectedBidderRequest,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {"bidder-request-ctx-1": "some-ctx-1"},
- "module-2": {"bidder-request-ctx-2": "some-ctx-2"},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("bidder-request-ctx-1", "some-ctx-1")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("bidder-request-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}},
expectedStageOutcomes: []StageOutcome{
{
@@ -1218,7 +1253,7 @@ func buildDefaultActivityConfig(componentName string, allow bool) config.Activit
}
func TestExecuteRawBidderResponseStage(t *testing.T) {
- foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}}
+ foobarModuleCtx := &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}}
resp := adapters.BidderResponse{Bids: []*adapters.TypedBid{{DealPriority: 1}}}
expResp := adapters.BidderResponse{Bids: []*adapters.TypedBid{{DealPriority: 10}}}
vEntity := entity("the-bidder")
@@ -1238,7 +1273,7 @@ func TestExecuteRawBidderResponseStage(t *testing.T) {
givenBidderResponse: resp,
expectedBidderResponse: resp,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{}},
expectedStageOutcomes: []StageOutcome{},
},
{
@@ -1357,9 +1392,18 @@ func TestExecuteRawBidderResponseStage(t *testing.T) {
givenBidderResponse: resp,
expectedBidderResponse: expResp,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {"raw-bidder-response-ctx-1": "some-ctx-1", "raw-bidder-response-ctx-3": "some-ctx-3"},
- "module-2": {"raw-bidder-response-ctx-2": "some-ctx-2"},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("raw-bidder-response-ctx-1", "some-ctx-1")
+ mc.Set("raw-bidder-response-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("raw-bidder-response-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}},
expectedStageOutcomes: []StageOutcome{
{
@@ -1435,7 +1479,7 @@ func TestExecuteRawBidderResponseStage(t *testing.T) {
}
func TestExecuteAllProcessedBidResponsesStage(t *testing.T) {
- foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}}
+ foobarModuleCtx := &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}}
expectedAllProcBidResponses := map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
"some-bidder": {Bids: []*entities.PbsOrtbBid{{DealPriority: 1}}},
@@ -1461,7 +1505,7 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) {
givenPlanBuilder: hooks.EmptyPlanBuilder{},
expectedBiddersResponse: expectedAllProcBidResponses,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{}},
expectedStageOutcomes: []StageOutcome{},
},
{
@@ -1642,9 +1686,17 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) {
givenPlanBuilder: TestWithModuleContextsPlanBuilder{},
expectedBiddersResponse: expectedAllProcBidResponses,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {"all-processed-bid-responses-ctx-1": "some-ctx-1"},
- "module-2": {"all-processed-bid-responses-ctx-2": "some-ctx-2"},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("all-processed-bid-responses-ctx-1", "some-ctx-1")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("all-processed-bid-responses-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}},
expectedStageOutcomes: []StageOutcome{
{
@@ -1709,7 +1761,7 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) {
}
func TestExecuteAuctionResponseStage(t *testing.T) {
- foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}}
+ foobarModuleCtx := &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}}
resp := &openrtb2.BidResponse{CustomData: "some-custom-data"}
expResp := &openrtb2.BidResponse{CustomData: "new-custom-data"}
@@ -1728,7 +1780,7 @@ func TestExecuteAuctionResponseStage(t *testing.T) {
givenResponse: resp,
expectedResponse: resp,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{}},
expectedStageOutcomes: []StageOutcome{},
},
{
@@ -1877,9 +1929,18 @@ func TestExecuteAuctionResponseStage(t *testing.T) {
givenResponse: resp,
expectedResponse: resp,
expectedReject: nil,
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {"auction-response-ctx-1": "some-ctx-1", "auction-response-ctx-3": "some-ctx-3"},
- "module-2": {"auction-response-ctx-2": "some-ctx-2"},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("auction-response-ctx-1", "some-ctx-1")
+ mc.Set("auction-response-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("auction-response-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}},
expectedStageOutcomes: []StageOutcome{
{
@@ -2000,7 +2061,7 @@ func TestExecuteExitpointStage(t *testing.T) {
expectedResponseHeaders: http.Header{
"Content-Type": []string{"application/json"},
},
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{}},
expectedStageOutcomes: []StageOutcome{},
},
{
@@ -2027,7 +2088,7 @@ func TestExecuteExitpointStage(t *testing.T) {
expectedResponseHeaders: http.Header{
"Content-Type": []string{"application/xml"},
},
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}},
expectedStageOutcomes: []StageOutcome{
{
Entity: "exitpoint",
@@ -2068,7 +2129,7 @@ func TestExecuteExitpointStage(t *testing.T) {
},
expectedResponse: ``,
expectedResponseHeaders: http.Header{"Content-Type": []string{"application/xml"}},
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}},
expectedStageOutcomes: []StageOutcome{
{
Entity: entityExitpoint,
@@ -2144,7 +2205,7 @@ func TestExecuteExitpointStage(t *testing.T) {
},
expectedResponse: ``,
expectedResponseHeaders: http.Header{"Content-Type": []string{"application/xml"}},
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{"foobar": nil}},
expectedStageOutcomes: []StageOutcome{
{
Entity: entityExitpoint,
@@ -2202,9 +2263,18 @@ func TestExecuteExitpointStage(t *testing.T) {
},
expectedResponse: &openrtb2.BidResponse{ID: "test-id"},
expectedResponseHeaders: http.Header{"Content-Type": []string{"application/json"}},
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {"exitpoint-ctx-1": "some-ctx-1", "exitpoint-ctx-3": "some-ctx-3"},
- "module-2": {"exitpoint-ctx-2": "some-ctx-2"},
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("exitpoint-ctx-1", "some-ctx-1")
+ mc.Set("exitpoint-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("exitpoint-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}},
expectedStageOutcomes: []StageOutcome{
{
@@ -2277,7 +2347,7 @@ func TestExecuteExitpointStage(t *testing.T) {
expectedResponseHeaders: http.Header{
"Content-Type": []string{"application/json"},
},
- expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
+ expectedModuleContexts: &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
"module-1": nil,
"module-2": nil,
}},
@@ -2345,12 +2415,18 @@ func TestInterStageContextCommunication(t *testing.T) {
assert.Nil(t, reject, "Unexpected reject from entrypoint stage.")
assert.Equal(
t,
- &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {
- "entrypoint-ctx-1": "some-ctx-1",
- "entrypoint-ctx-3": "some-ctx-3",
- },
- "module-2": {"entrypoint-ctx-2": "some-ctx-2"},
+ &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-1", "some-ctx-1")
+ mc.Set("entrypoint-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}},
exec.moduleContexts,
"Wrong module contexts after executing entrypoint hook.",
@@ -2359,83 +2435,99 @@ func TestInterStageContextCommunication(t *testing.T) {
// test that context added at the raw-auction stage merged with existing module contexts
_, reject = exec.ExecuteRawAuctionStage(body)
assert.Nil(t, reject, "Unexpected reject from raw-auction stage.")
- assert.Equal(t, &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {
- "entrypoint-ctx-1": "some-ctx-1",
- "entrypoint-ctx-3": "some-ctx-3",
- "raw-auction-ctx-1": "some-ctx-1",
- "raw-auction-ctx-3": "some-ctx-3",
- },
- "module-2": {
- "entrypoint-ctx-2": "some-ctx-2",
- "raw-auction-ctx-2": "some-ctx-2",
- },
+ assert.Equal(t, &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-1", "some-ctx-1")
+ mc.Set("entrypoint-ctx-3", "some-ctx-3")
+ mc.Set("raw-auction-ctx-1", "some-ctx-1")
+ mc.Set("raw-auction-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-2", "some-ctx-2")
+ mc.Set("raw-auction-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}}, exec.moduleContexts, "Wrong module contexts after executing raw-auction hook.")
// test that context added at the processed-auction stage merged with existing module contexts
err = exec.ExecuteProcessedAuctionStage(&openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{}})
assert.Nil(t, err, "Unexpected reject from processed-auction stage.")
- assert.Equal(t, &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {
- "entrypoint-ctx-1": "some-ctx-1",
- "entrypoint-ctx-3": "some-ctx-3",
- "raw-auction-ctx-1": "some-ctx-1",
- "raw-auction-ctx-3": "some-ctx-3",
- "processed-auction-ctx-1": "some-ctx-1",
- "processed-auction-ctx-3": "some-ctx-3",
- },
- "module-2": {
- "entrypoint-ctx-2": "some-ctx-2",
- "raw-auction-ctx-2": "some-ctx-2",
- "processed-auction-ctx-2": "some-ctx-2",
- },
+ assert.Equal(t, &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-1", "some-ctx-1")
+ mc.Set("entrypoint-ctx-3", "some-ctx-3")
+ mc.Set("raw-auction-ctx-1", "some-ctx-1")
+ mc.Set("raw-auction-ctx-3", "some-ctx-3")
+ mc.Set("processed-auction-ctx-1", "some-ctx-1")
+ mc.Set("processed-auction-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-2", "some-ctx-2")
+ mc.Set("raw-auction-ctx-2", "some-ctx-2")
+ mc.Set("processed-auction-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}}, exec.moduleContexts, "Wrong module contexts after executing processed-auction hook.")
// test that context added at the raw bidder response stage merged with existing module contexts
reject = exec.ExecuteRawBidderResponseStage(&adapters.BidderResponse{}, "some-bidder")
assert.Nil(t, reject, "Unexpected reject from raw-bidder-response stage.")
- assert.Equal(t, &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {
- "entrypoint-ctx-1": "some-ctx-1",
- "entrypoint-ctx-3": "some-ctx-3",
- "raw-auction-ctx-1": "some-ctx-1",
- "raw-auction-ctx-3": "some-ctx-3",
- "processed-auction-ctx-1": "some-ctx-1",
- "processed-auction-ctx-3": "some-ctx-3",
- "raw-bidder-response-ctx-1": "some-ctx-1",
- "raw-bidder-response-ctx-3": "some-ctx-3",
- },
- "module-2": {
- "entrypoint-ctx-2": "some-ctx-2",
- "raw-auction-ctx-2": "some-ctx-2",
- "processed-auction-ctx-2": "some-ctx-2",
- "raw-bidder-response-ctx-2": "some-ctx-2",
- },
+ assert.Equal(t, &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-1", "some-ctx-1")
+ mc.Set("entrypoint-ctx-3", "some-ctx-3")
+ mc.Set("raw-auction-ctx-1", "some-ctx-1")
+ mc.Set("raw-auction-ctx-3", "some-ctx-3")
+ mc.Set("processed-auction-ctx-1", "some-ctx-1")
+ mc.Set("processed-auction-ctx-3", "some-ctx-3")
+ mc.Set("raw-bidder-response-ctx-1", "some-ctx-1")
+ mc.Set("raw-bidder-response-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-2", "some-ctx-2")
+ mc.Set("raw-auction-ctx-2", "some-ctx-2")
+ mc.Set("processed-auction-ctx-2", "some-ctx-2")
+ mc.Set("raw-bidder-response-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}}, exec.moduleContexts, "Wrong module contexts after executing raw-bidder-response hook.")
// test that context added at the auction-response stage merged with existing module contexts
exec.ExecuteAuctionResponseStage(&openrtb2.BidResponse{})
assert.Nil(t, reject, "Unexpected reject from raw-auction stage.")
- assert.Equal(t, &moduleContexts{ctxs: map[string]hookstage.ModuleContext{
- "module-1": {
- "entrypoint-ctx-1": "some-ctx-1",
- "entrypoint-ctx-3": "some-ctx-3",
- "raw-auction-ctx-1": "some-ctx-1",
- "raw-auction-ctx-3": "some-ctx-3",
- "processed-auction-ctx-1": "some-ctx-1",
- "processed-auction-ctx-3": "some-ctx-3",
- "raw-bidder-response-ctx-1": "some-ctx-1",
- "raw-bidder-response-ctx-3": "some-ctx-3",
- "auction-response-ctx-1": "some-ctx-1",
- "auction-response-ctx-3": "some-ctx-3",
- },
- "module-2": {
- "entrypoint-ctx-2": "some-ctx-2",
- "raw-auction-ctx-2": "some-ctx-2",
- "processed-auction-ctx-2": "some-ctx-2",
- "raw-bidder-response-ctx-2": "some-ctx-2",
- "auction-response-ctx-2": "some-ctx-2",
- },
+ assert.Equal(t, &moduleContexts{ctxs: map[string]*hookstage.ModuleContext{
+ "module-1": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-1", "some-ctx-1")
+ mc.Set("entrypoint-ctx-3", "some-ctx-3")
+ mc.Set("raw-auction-ctx-1", "some-ctx-1")
+ mc.Set("raw-auction-ctx-3", "some-ctx-3")
+ mc.Set("processed-auction-ctx-1", "some-ctx-1")
+ mc.Set("processed-auction-ctx-3", "some-ctx-3")
+ mc.Set("raw-bidder-response-ctx-1", "some-ctx-1")
+ mc.Set("raw-bidder-response-ctx-3", "some-ctx-3")
+ mc.Set("auction-response-ctx-1", "some-ctx-1")
+ mc.Set("auction-response-ctx-3", "some-ctx-3")
+ return mc
+ }(),
+ "module-2": func() *hookstage.ModuleContext {
+ mc := hookstage.NewModuleContext()
+ mc.Set("entrypoint-ctx-2", "some-ctx-2")
+ mc.Set("raw-auction-ctx-2", "some-ctx-2")
+ mc.Set("processed-auction-ctx-2", "some-ctx-2")
+ mc.Set("raw-bidder-response-ctx-2", "some-ctx-2")
+ mc.Set("auction-response-ctx-2", "some-ctx-2")
+ return mc
+ }(),
}}, exec.moduleContexts, "Wrong module contexts after executing auction-response hook.")
}
diff --git a/hooks/hookexecution/mocks_test.go b/hooks/hookexecution/mocks_test.go
index cb3c95b9d1f..a5857157d56 100644
--- a/hooks/hookexecution/mocks_test.go
+++ b/hooks/hookexecution/mocks_test.go
@@ -201,42 +201,50 @@ type mockModuleContextHook struct {
}
func (e mockModuleContextHook) HandleEntrypointHook(_ context.Context, miCtx hookstage.ModuleInvocationContext, _ hookstage.EntrypointPayload) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
- miCtx.ModuleContext = map[string]interface{}{e.key: e.val}
+ miCtx.ModuleContext = hookstage.NewModuleContext()
+ miCtx.ModuleContext.Set(e.key, e.val)
return hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: miCtx.ModuleContext}, nil
}
func (e mockModuleContextHook) HandleRawAuctionHook(_ context.Context, miCtx hookstage.ModuleInvocationContext, _ hookstage.RawAuctionRequestPayload) (hookstage.HookResult[hookstage.RawAuctionRequestPayload], error) {
- miCtx.ModuleContext = map[string]interface{}{e.key: e.val}
+ miCtx.ModuleContext = hookstage.NewModuleContext()
+ miCtx.ModuleContext.Set(e.key, e.val)
return hookstage.HookResult[hookstage.RawAuctionRequestPayload]{ModuleContext: miCtx.ModuleContext}, nil
}
func (e mockModuleContextHook) HandleProcessedAuctionHook(_ context.Context, miCtx hookstage.ModuleInvocationContext, _ hookstage.ProcessedAuctionRequestPayload) (hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload], error) {
- miCtx.ModuleContext = map[string]interface{}{e.key: e.val}
+ miCtx.ModuleContext = hookstage.NewModuleContext()
+ miCtx.ModuleContext.Set(e.key, e.val)
return hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload]{ModuleContext: miCtx.ModuleContext}, nil
}
func (e mockModuleContextHook) HandleBidderRequestHook(_ context.Context, miCtx hookstage.ModuleInvocationContext, _ hookstage.BidderRequestPayload) (hookstage.HookResult[hookstage.BidderRequestPayload], error) {
- miCtx.ModuleContext = map[string]interface{}{e.key: e.val}
+ miCtx.ModuleContext = hookstage.NewModuleContext()
+ miCtx.ModuleContext.Set(e.key, e.val)
return hookstage.HookResult[hookstage.BidderRequestPayload]{ModuleContext: miCtx.ModuleContext}, nil
}
func (e mockModuleContextHook) HandleRawBidderResponseHook(_ context.Context, miCtx hookstage.ModuleInvocationContext, _ hookstage.RawBidderResponsePayload) (hookstage.HookResult[hookstage.RawBidderResponsePayload], error) {
- miCtx.ModuleContext = map[string]interface{}{e.key: e.val}
+ miCtx.ModuleContext = hookstage.NewModuleContext()
+ miCtx.ModuleContext.Set(e.key, e.val)
return hookstage.HookResult[hookstage.RawBidderResponsePayload]{ModuleContext: miCtx.ModuleContext}, nil
}
func (e mockModuleContextHook) HandleAllProcessedBidResponsesHook(_ context.Context, miCtx hookstage.ModuleInvocationContext, _ hookstage.AllProcessedBidResponsesPayload) (hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload], error) {
- miCtx.ModuleContext = map[string]interface{}{e.key: e.val}
+ miCtx.ModuleContext = hookstage.NewModuleContext()
+ miCtx.ModuleContext.Set(e.key, e.val)
return hookstage.HookResult[hookstage.AllProcessedBidResponsesPayload]{ModuleContext: miCtx.ModuleContext}, nil
}
func (e mockModuleContextHook) HandleAuctionResponseHook(_ context.Context, miCtx hookstage.ModuleInvocationContext, _ hookstage.AuctionResponsePayload) (hookstage.HookResult[hookstage.AuctionResponsePayload], error) {
- miCtx.ModuleContext = map[string]interface{}{e.key: e.val}
+ miCtx.ModuleContext = hookstage.NewModuleContext()
+ miCtx.ModuleContext.Set(e.key, e.val)
return hookstage.HookResult[hookstage.AuctionResponsePayload]{ModuleContext: miCtx.ModuleContext}, nil
}
func (e mockModuleContextHook) HandleExitpointHook(_ context.Context, miCtx hookstage.ModuleInvocationContext, _ hookstage.ExitpointPayload) (hookstage.HookResult[hookstage.ExitpointPayload], error) {
- miCtx.ModuleContext = map[string]interface{}{e.key: e.val}
+ miCtx.ModuleContext = hookstage.NewModuleContext()
+ miCtx.ModuleContext.Set(e.key, e.val)
return hookstage.HookResult[hookstage.ExitpointPayload]{ModuleContext: miCtx.ModuleContext}, nil
}
diff --git a/hooks/hookstage/invocation.go b/hooks/hookstage/invocation.go
index ee2f2ec89a6..b0b1c00089e 100644
--- a/hooks/hookstage/invocation.go
+++ b/hooks/hookstage/invocation.go
@@ -2,6 +2,8 @@ package hookstage
import (
"encoding/json"
+ "maps"
+ "sync"
"github.com/prebid/prebid-server/v4/hooks/hookanalytics"
)
@@ -16,7 +18,7 @@ type HookResult[T any] struct {
Warnings []string
DebugMessages []string
AnalyticsTags hookanalytics.Analytics
- ModuleContext ModuleContext // holds values that the module wants to pass to itself at later stages
+ ModuleContext *ModuleContext // holds values that the module wants to pass to itself at later stages
}
// ModuleInvocationContext holds data passed to the module hook during invocation.
@@ -28,11 +30,77 @@ type ModuleInvocationContext struct {
// Endpoint represents the path of the current endpoint.
Endpoint string
// ModuleContext holds values that the module passes to itself from the previous stages.
- ModuleContext ModuleContext
+ ModuleContext *ModuleContext
// HookImplCode is the hook_impl_code for a module instance to differentiate between multiple hooks
HookImplCode string
}
// ModuleContext holds arbitrary data passed between module hooks at different stages.
// We use interface as we do not know exactly how the modules will use their inner context.
-type ModuleContext map[string]interface{}
+type ModuleContext struct {
+ sync.RWMutex
+ data map[string]any
+}
+
+// NewModuleContext creates a new module context
+func NewModuleContext() *ModuleContext {
+ moduleContext := ModuleContext{
+ data: make(map[string]any),
+ }
+ return &moduleContext
+}
+
+// Get retrieves a value from the module context with read lock
+func (mc *ModuleContext) Get(key string) (any, bool) {
+ if mc == nil {
+ return nil, false
+ }
+ mc.RLock()
+ defer mc.RUnlock()
+ if mc.data == nil {
+ return nil, false
+ }
+ val, ok := mc.data[key]
+ return val, ok
+}
+
+// Set stores a value in the module context with write lock
+func (mc *ModuleContext) Set(key string, value any) {
+ if mc == nil {
+ return
+ }
+ mc.Lock()
+ defer mc.Unlock()
+ if mc.data == nil {
+ mc.data = make(map[string]any)
+ }
+ mc.data[key] = value
+}
+
+// GetAll returns a copy of all data in the context
+func (mc *ModuleContext) GetAll() map[string]any {
+ if mc == nil {
+ return nil
+ }
+ mc.RLock()
+ defer mc.RUnlock()
+ if mc.data == nil {
+ return nil
+ }
+ result := make(map[string]any, len(mc.data))
+ maps.Copy(result, mc.data)
+ return result
+}
+
+// SetAll merges the provided data into the module context
+func (mc *ModuleContext) SetAll(data map[string]any) {
+ if mc == nil {
+ return
+ }
+ mc.Lock()
+ defer mc.Unlock()
+ if mc.data == nil {
+ mc.data = make(map[string]any, len(data))
+ }
+ maps.Copy(mc.data, data)
+}
diff --git a/modules/fiftyonedegrees/devicedetection/evidence_extractor.go b/modules/fiftyonedegrees/devicedetection/evidence_extractor.go
index 511b85960b7..9599e43de15 100644
--- a/modules/fiftyonedegrees/devicedetection/evidence_extractor.go
+++ b/modules/fiftyonedegrees/devicedetection/evidence_extractor.go
@@ -57,16 +57,18 @@ func merge(val1, val2 []stringEvidence) []stringEvidence {
return evidence
}
-func (x *defaultEvidenceExtractor) extract(ctx hookstage.ModuleContext) ([]onpremise.Evidence, string, error) {
+func (x *defaultEvidenceExtractor) extract(ctx *hookstage.ModuleContext) ([]onpremise.Evidence, string, error) {
if ctx == nil {
return nil, "", errors.New("context is nil")
}
- suaStrings, err := x.getEvidenceStrings(ctx[evidenceFromSuaCtxKey])
+ evidenceFromSuaCtx, _ := ctx.Get(evidenceFromSuaCtxKey)
+ suaStrings, err := x.getEvidenceStrings(evidenceFromSuaCtx)
if err != nil {
return nil, "", fmt.Errorf("error extracting sua evidence: %w", err)
}
- headerString, err := x.getEvidenceStrings(ctx[evidenceFromHeadersCtxKey])
+ evidenceFromHeadersCtx, _ := ctx.Get(evidenceFromHeadersCtxKey)
+ headerString, err := x.getEvidenceStrings(evidenceFromHeadersCtx)
if err != nil {
return nil, "", fmt.Errorf("error extracting header evidence: %w", err)
}
diff --git a/modules/fiftyonedegrees/devicedetection/evidence_extractor_test.go b/modules/fiftyonedegrees/devicedetection/evidence_extractor_test.go
index 7e2aa6616d8..8c183af559d 100644
--- a/modules/fiftyonedegrees/devicedetection/evidence_extractor_test.go
+++ b/modules/fiftyonedegrees/devicedetection/evidence_extractor_test.go
@@ -129,7 +129,7 @@ func TestExtract(t *testing.T) {
tests := []struct {
name string
- ctx hookstage.ModuleContext
+ ctx *hookstage.ModuleContext
wantEvidenceCount int
wantUserAgent string
wantError bool
@@ -141,96 +141,120 @@ func TestExtract(t *testing.T) {
},
{
name: "empty",
- ctx: hookstage.ModuleContext{
- evidenceFromSuaCtxKey: []stringEvidence{},
- evidenceFromHeadersCtxKey: []stringEvidence{},
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromHeadersCtxKey, []stringEvidence{})
+ ctx.Set(evidenceFromSuaCtxKey, []stringEvidence{})
+ return ctx
+ }(),
wantEvidenceCount: 0,
wantUserAgent: "",
},
{
name: "from_headers",
- ctx: hookstage.ModuleContext{
- evidenceFromHeadersCtxKey: []stringEvidence{uaEvidence1},
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromHeadersCtxKey, []stringEvidence{uaEvidence1})
+ return ctx
+ }(),
wantEvidenceCount: 1,
wantUserAgent: "uav1",
},
{
name: "from_headers_no_user_agent",
- ctx: hookstage.ModuleContext{
- evidenceFromHeadersCtxKey: []stringEvidence{evidence1},
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromHeadersCtxKey, []stringEvidence{evidence1})
+ return ctx
+ }(),
wantError: true,
},
{
name: "from_sua",
- ctx: hookstage.ModuleContext{
- evidenceFromSuaCtxKey: []stringEvidence{uaEvidence1},
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromSuaCtxKey, []stringEvidence{uaEvidence1})
+ return ctx
+ }(),
wantEvidenceCount: 1,
wantUserAgent: "uav1",
},
{
name: "from_sua_no_user_agent",
- ctx: hookstage.ModuleContext{
- evidenceFromSuaCtxKey: []stringEvidence{evidence1},
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromSuaCtxKey, []stringEvidence{evidence1})
+ return ctx
+ }(),
wantError: true,
},
{
name: "from_headers_error",
- ctx: hookstage.ModuleContext{
- evidenceFromHeadersCtxKey: "bad value",
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromHeadersCtxKey, "bad value")
+ return ctx
+ }(),
wantError: true,
},
{
name: "from_sua_error",
- ctx: hookstage.ModuleContext{
- evidenceFromHeadersCtxKey: []stringEvidence{},
- evidenceFromSuaCtxKey: "bad value",
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromHeadersCtxKey, []stringEvidence{})
+ ctx.Set(evidenceFromSuaCtxKey, "bad value")
+ return ctx
+ }(),
wantError: true,
},
{
name: "from_sua_and_headers",
- ctx: hookstage.ModuleContext{
- evidenceFromHeadersCtxKey: []stringEvidence{uaEvidence1},
- evidenceFromSuaCtxKey: []stringEvidence{evidence1},
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromHeadersCtxKey, []stringEvidence{uaEvidence1})
+ ctx.Set(evidenceFromSuaCtxKey, []stringEvidence{evidence1})
+ return ctx
+ }(),
wantEvidenceCount: 2,
wantUserAgent: "uav1",
},
{
name: "from_sua_and_headers_sua_can_overwrite_if_ua_present",
- ctx: hookstage.ModuleContext{
- evidenceFromHeadersCtxKey: []stringEvidence{uaEvidence1},
- evidenceFromSuaCtxKey: []stringEvidence{uaEvidence2},
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromHeadersCtxKey, []stringEvidence{uaEvidence1})
+ ctx.Set(evidenceFromSuaCtxKey, []stringEvidence{uaEvidence2})
+ return ctx
+ }(),
wantEvidenceCount: 1,
wantUserAgent: "uav2",
},
{
name: "empty_string_values",
- ctx: hookstage.ModuleContext{
- evidenceFromHeadersCtxKey: []stringEvidence{emptyEvidence},
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromHeadersCtxKey, []stringEvidence{emptyEvidence})
+ return ctx
+ }(),
wantError: true,
},
{
name: "empty_sua_values",
- ctx: hookstage.ModuleContext{
- evidenceFromSuaCtxKey: []stringEvidence{emptyEvidence},
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromSuaCtxKey, []stringEvidence{emptyEvidence})
+ return ctx
+ }(),
wantError: true,
},
{
name: "mixed_valid_and_invalid",
- ctx: hookstage.ModuleContext{
- evidenceFromHeadersCtxKey: []stringEvidence{uaEvidence1},
- evidenceFromSuaCtxKey: "bad value",
- },
+ ctx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(evidenceFromHeadersCtxKey, []stringEvidence{uaEvidence1})
+ ctx.Set(evidenceFromSuaCtxKey, "bad value")
+ return ctx
+ }(),
wantError: true,
},
}
diff --git a/modules/fiftyonedegrees/devicedetection/hook_auction_entrypoint.go b/modules/fiftyonedegrees/devicedetection/hook_auction_entrypoint.go
index f1429ef7e1e..996845a2f0c 100644
--- a/modules/fiftyonedegrees/devicedetection/hook_auction_entrypoint.go
+++ b/modules/fiftyonedegrees/devicedetection/hook_auction_entrypoint.go
@@ -18,10 +18,10 @@ func handleAuctionEntryPointRequestHook(cfg config, payload hookstage.Entrypoint
evidenceFromSua := evidenceExtractor.fromSuaPayload(payload.Body)
// create a Module context and set the evidence from headers, evidence from sua and dd enabled flag
- moduleContext := make(hookstage.ModuleContext)
- moduleContext[evidenceFromHeadersCtxKey] = evidenceFromHeaders
- moduleContext[evidenceFromSuaCtxKey] = evidenceFromSua
- moduleContext[ddEnabledCtxKey] = true
+ moduleContext := hookstage.NewModuleContext()
+ moduleContext.Set(evidenceFromHeadersCtxKey, evidenceFromHeaders)
+ moduleContext.Set(evidenceFromSuaCtxKey, evidenceFromSua)
+ moduleContext.Set(ddEnabledCtxKey, true)
return hookstage.HookResult[hookstage.EntrypointPayload]{
ModuleContext: moduleContext,
diff --git a/modules/fiftyonedegrees/devicedetection/module.go b/modules/fiftyonedegrees/devicedetection/module.go
index 72c0f60d94f..4dddc577f4b 100644
--- a/modules/fiftyonedegrees/devicedetection/module.go
+++ b/modules/fiftyonedegrees/devicedetection/module.go
@@ -83,7 +83,7 @@ type accountValidator interface {
type evidenceExtractor interface {
fromHeaders(request *http.Request, httpHeaderKeys []dd.EvidenceKey) []stringEvidence
fromSuaPayload(payload []byte) []stringEvidence
- extract(ctx hookstage.ModuleContext) ([]onpremise.Evidence, string, error)
+ extract(ctx *hookstage.ModuleContext) ([]onpremise.Evidence, string, error)
}
func (m Module) HandleEntrypointHook(
diff --git a/modules/fiftyonedegrees/devicedetection/module_test.go b/modules/fiftyonedegrees/devicedetection/module_test.go
index 5566f8e25ac..7f757cd42d4 100644
--- a/modules/fiftyonedegrees/devicedetection/module_test.go
+++ b/modules/fiftyonedegrees/devicedetection/module_test.go
@@ -44,7 +44,7 @@ func (m *mockEvidenceExtractor) fromSuaPayload(payload []byte) []stringEvidence
return args.Get(0).([]stringEvidence)
}
-func (m *mockEvidenceExtractor) extract(ctx hookstage.ModuleContext) ([]onpremise.Evidence, string, error) {
+func (m *mockEvidenceExtractor) extract(ctx *hookstage.ModuleContext) ([]onpremise.Evidence, string, error) {
args := m.Called(ctx)
res := args.Get(0)
@@ -131,16 +131,18 @@ func TestHandleEntrypointHookAccountAllowed(t *testing.T) {
result, err := module.HandleEntrypointHook(nil, hookstage.ModuleInvocationContext{}, hookstage.EntrypointPayload{})
assert.NoError(t, err)
+ evidenceFromHeadersCtx, _ := result.ModuleContext.Get(evidenceFromHeadersCtxKey)
assert.Equal(
- t, result.ModuleContext[evidenceFromHeadersCtxKey], []stringEvidence{{
+ t, evidenceFromHeadersCtx, []stringEvidence{{
Prefix: "123",
Key: "key",
Value: "val",
}},
)
+ evidenceFromSuaCtx, _ := result.ModuleContext.Get(evidenceFromSuaCtxKey)
assert.Equal(
- t, result.ModuleContext[evidenceFromSuaCtxKey], []stringEvidence{{
+ t, evidenceFromSuaCtx, []stringEvidence{{
Prefix: "123",
Key: "User-Agent",
Value: "ua",
@@ -179,9 +181,8 @@ func TestHandleRawAuctionHookExtractError(t *testing.T) {
accountValidator: &mockValidator,
}
- mctx := make(hookstage.ModuleContext)
-
- mctx[ddEnabledCtxKey] = true
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(ddEnabledCtxKey, true)
result, err := module.HandleRawAuctionHook(
context.TODO(), hookstage.ModuleInvocationContext{
@@ -280,8 +281,8 @@ func TestHandleRawAuctionHookEnrichment(t *testing.T) {
accountValidator: &mockValidator,
}
- mctx := make(hookstage.ModuleContext)
- mctx[ddEnabledCtxKey] = true
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(ddEnabledCtxKey, true)
result, err := module.HandleRawAuctionHook(
nil, hookstage.ModuleInvocationContext{
@@ -483,8 +484,8 @@ func TestHandleRawAuctionHookEnrichmentWithErrors(t *testing.T) {
accountValidator: &mockValidator,
}
- mctx := make(hookstage.ModuleContext)
- mctx[ddEnabledCtxKey] = true
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(ddEnabledCtxKey, true)
result, err := module.HandleRawAuctionHook(
nil, hookstage.ModuleInvocationContext{
diff --git a/modules/prebid/ortb2blocking/hook_bidderrequest.go b/modules/prebid/ortb2blocking/hook_bidderrequest.go
index 14abbec43ab..9542777ae08 100644
--- a/modules/prebid/ortb2blocking/hook_bidderrequest.go
+++ b/modules/prebid/ortb2blocking/hook_bidderrequest.go
@@ -47,7 +47,10 @@ func handleBidderRequestHook(
updateCatTax(cfg, payload, &blockingAttributes, &changeSet)
result.ChangeSet = changeSet
- result.ModuleContext = hookstage.ModuleContext{payload.Bidder: blockingAttributes}
+ if result.ModuleContext == nil {
+ result.ModuleContext = hookstage.NewModuleContext()
+ }
+ result.ModuleContext.Set(payload.Bidder, blockingAttributes)
return result, nil
}
diff --git a/modules/prebid/ortb2blocking/hook_raw_bidder_response.go b/modules/prebid/ortb2blocking/hook_raw_bidder_response.go
index deeb3b02967..cbdae937352 100644
--- a/modules/prebid/ortb2blocking/hook_raw_bidder_response.go
+++ b/modules/prebid/ortb2blocking/hook_raw_bidder_response.go
@@ -15,10 +15,10 @@ import (
func handleRawBidderResponseHook(
cfg config,
payload hookstage.RawBidderResponsePayload,
- moduleCtx hookstage.ModuleContext,
+ moduleCtx *hookstage.ModuleContext,
) (result hookstage.HookResult[hookstage.RawBidderResponsePayload], err error) {
bidder := payload.Bidder
- blockAttrsVal, ok := moduleCtx[bidder]
+ blockAttrsVal, ok := moduleCtx.Get(bidder)
if !ok {
// if there are no blocking attributes for this bidder just pass empty blockingAttributes for further processing
// other values from config must still be checked
diff --git a/modules/prebid/ortb2blocking/module_test.go b/modules/prebid/ortb2blocking/module_test.go
index 7d002d29c8e..85be537bb6c 100644
--- a/modules/prebid/ortb2blocking/module_test.go
+++ b/modules/prebid/ortb2blocking/module_test.go
@@ -343,8 +343,9 @@ func TestHandleBidderRequestHook(t *testing.T) {
},
},
expectedHookResult: hookstage.HookResult[hookstage.BidderRequestPayload]{
- ModuleContext: map[string]interface{}{
- bidder: blockingAttributes{
+ ModuleContext: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{
bAdv: []string{bAdvA, bAdvB},
bApp: []string{bApp3},
bCat: []string{bCat1, bCat2, bCat3, bCat4},
@@ -357,8 +358,9 @@ func TestHandleBidderRequestHook(t *testing.T) {
"ImpID2": toInt([]adcom1.CreativeAttribute{bAttr1, bAttr8, bAttr9, bAttr10}),
},
catTax: catTax,
- },
- },
+ })
+ return mctx
+ }(),
Warnings: []string{
// multiple warnings may be added (per condition)
"More than one condition matches request. Bidder: appnexus, request media types: audio, banner, native, video",
@@ -389,12 +391,14 @@ func TestHandleBidderRequestHook(t *testing.T) {
},
},
expectedHookResult: hookstage.HookResult[hookstage.BidderRequestPayload]{
- ModuleContext: map[string]interface{}{
- bidder: blockingAttributes{
+ ModuleContext: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{
bAdv: []string{bAdvA, bAdvB, bAdvC},
bAttr: map[string][]int{},
- },
- },
+ })
+ return mctx
+ }(),
},
expectedError: nil,
},
@@ -439,9 +443,13 @@ func TestHandleBidderRequestHook(t *testing.T) {
},
},
expectedHookResult: hookstage.HookResult[hookstage.BidderRequestPayload]{
- ModuleContext: map[string]interface{}{bidder: blockingAttributes{
- bAttr: map[string][]int{},
- }},
+ ModuleContext: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{
+ bAttr: map[string][]int{},
+ })
+ return mctx
+ }(),
},
expectedError: nil,
},
@@ -459,9 +467,13 @@ func TestHandleBidderRequestHook(t *testing.T) {
Imp: []openrtb2.Imp{{ID: "ImpID1", Video: &openrtb2.Video{}}},
},
expectedHookResult: hookstage.HookResult[hookstage.BidderRequestPayload]{
- ModuleContext: map[string]interface{}{bidder: blockingAttributes{
- bAttr: map[string][]int{},
- }},
+ ModuleContext: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{
+ bAttr: map[string][]int{},
+ })
+ return mctx
+ }(),
},
expectedError: nil,
},
@@ -579,7 +591,7 @@ func TestHandleBidderRequestHook(t *testing.T) {
hookstage.ModuleInvocationContext{
AccountConfig: test.config,
Endpoint: hookexecution.EndpointAuction,
- ModuleContext: map[string]interface{}{},
+ ModuleContext: hookstage.NewModuleContext(),
},
payload,
)
@@ -605,7 +617,7 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
description string
payload hookstage.RawBidderResponsePayload
config json.RawMessage
- moduleCtx hookstage.ModuleContext
+ moduleCtx *hookstage.ModuleContext
expectedBids []*adapters.TypedBid
expectedHookResult hookstage.HookResult[hookstage.RawBidderResponsePayload]
expectedError error
@@ -655,7 +667,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ADomain: []string{"foo"}, ImpID: impID1},
},
},
- moduleCtx: map[string]interface{}{bidder: "boo"},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, "boo")
+ return mctx
+ }(),
expectedError: hookexecution.NewFailure("could not cast blocking attributes for bidder `appnexus`, module context has incorrect data"),
},
{
@@ -676,7 +692,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{bAdv: []string{"forbidden_domain"}})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -724,7 +744,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{"other-bidder": blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set("other-bidder", blockingAttributes{bAdv: []string{"forbidden_domain"}})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -767,7 +791,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{bAdv: []string{"forbidden_domain"}})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -810,7 +838,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{bAdv: []string{"forbidden_domain"}})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -850,7 +882,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -898,7 +934,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -941,7 +981,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", ADomain: []string{"good_domain"}, ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAdv: []string{"forbidden_domain"}}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{bAdv: []string{"forbidden_domain"}})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -1062,7 +1106,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", Cat: []string{"moto"}, ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{bCat: []string{"fishing"}}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{bCat: []string{"fishing"}})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -1188,7 +1236,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", CatTax: 2, ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{catTax: 2}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{catTax: 2})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -1277,7 +1329,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", Bundle: "allowed_bundle", ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{bApp: []string{"forbidden_bundle"}}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{bApp: []string{"forbidden_bundle"}})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -1376,7 +1432,11 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
Bid: &openrtb2.Bid{ID: "2", Attr: []adcom1.CreativeAttribute{2}, ImpID: impID2},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{bAttr: map[string][]int{impID1: {1}}}},
+ moduleCtx: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set(bidder, blockingAttributes{bAttr: map[string][]int{impID1: {1}}})
+ return mctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
@@ -1499,13 +1559,17 @@ func TestHandleRawBidderResponseHook(t *testing.T) {
},
},
},
- moduleCtx: map[string]interface{}{bidder: blockingAttributes{
- bAdv: []string{"forbidden_domain"},
- bCat: []string{"fishing"},
- catTax: 2,
- bApp: []string{"forbidden_bundle"},
- bAttr: map[string][]int{impID1: {1}}},
- },
+ moduleCtx: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(bidder, blockingAttributes{
+ bAdv: []string{"forbidden_domain"},
+ bCat: []string{"fishing"},
+ catTax: 2,
+ bApp: []string{"forbidden_bundle"},
+ bAttr: map[string][]int{impID1: {1}},
+ })
+ return ctx
+ }(),
expectedHookResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{
AnalyticsTags: hookanalytics.Analytics{
Activities: []hookanalytics.Activity{
diff --git a/modules/scientiamobile/wurfl_devicedetection/module.go b/modules/scientiamobile/wurfl_devicedetection/module.go
index 53bb26e74d9..7cbf8d3a6db 100644
--- a/modules/scientiamobile/wurfl_devicedetection/module.go
+++ b/modules/scientiamobile/wurfl_devicedetection/module.go
@@ -75,8 +75,8 @@ func (m Module) HandleEntrypointHook(ctx context.Context, invocationCtx hookstag
header[k] = payload.Request.Header.Get(k)
}
}
- moduleContext := make(hookstage.ModuleContext)
- moduleContext[wurflHeaderCtxKey] = header
+ moduleContext := hookstage.NewModuleContext()
+ moduleContext.Set(wurflHeaderCtxKey, header)
result.ModuleContext = moduleContext
return result, nil
@@ -101,7 +101,11 @@ func (m Module) HandleRawAuctionHook(
return result, nil
}
- rawHeaders, ok := invocationCtx.ModuleContext[wurflHeaderCtxKey].(map[string]string)
+ headers, ok := invocationCtx.ModuleContext.Get(wurflHeaderCtxKey)
+ if !ok {
+ return result, hookexecution.NewFailure("invalid module context type")
+ }
+ rawHeaders, ok := headers.(map[string]string)
if !ok {
return result, hookexecution.NewFailure("invalid module context type")
}
diff --git a/modules/scientiamobile/wurfl_devicedetection/module_test.go b/modules/scientiamobile/wurfl_devicedetection/module_test.go
index 64aaefe7fde..8dc0ee48ee7 100644
--- a/modules/scientiamobile/wurfl_devicedetection/module_test.go
+++ b/modules/scientiamobile/wurfl_devicedetection/module_test.go
@@ -150,7 +150,8 @@ func TestHandleEntrypointHook(t *testing.T) {
} else {
assert.NoError(t, err)
assert.NotNil(t, result.ModuleContext)
- assert.Equal(t, tc.expectedModuleCtx[wurflHeaderCtxKey], result.ModuleContext[wurflHeaderCtxKey])
+ headers, _ := result.ModuleContext.Get(wurflHeaderCtxKey)
+ assert.Equal(t, tc.expectedModuleCtx[wurflHeaderCtxKey], headers)
}
})
}
@@ -184,11 +185,13 @@ func TestHandleRawAuctionHook(t *testing.T) {
extCaps: false,
},
invocationCtx: hookstage.ModuleInvocationContext{
- ModuleContext: hookstage.ModuleContext{
- wurflHeaderCtxKey: map[string]string{
+ ModuleContext: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(wurflHeaderCtxKey, map[string]string{
"User-Agent": "Mozilla/5.0",
- },
- },
+ })
+ return ctx
+ }(),
},
payload: []byte(`{"device":{"ua":"Mozilla/5.0"}}`),
expectedErr: false,
@@ -242,11 +245,13 @@ func TestHandleRawAuctionHook(t *testing.T) {
extCaps: true,
},
invocationCtx: hookstage.ModuleInvocationContext{
- ModuleContext: hookstage.ModuleContext{
- wurflHeaderCtxKey: map[string]string{
+ ModuleContext: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(wurflHeaderCtxKey, map[string]string{
"User-Agent": "Mozilla/5.0",
- },
- },
+ })
+ return ctx
+ }(),
},
payload: []byte(`{"device":{"ua":"Mozilla/5.0"}}`),
expectedErr: false,
@@ -288,11 +293,13 @@ func TestHandleRawAuctionHook(t *testing.T) {
extCaps: true,
},
invocationCtx: hookstage.ModuleInvocationContext{
- ModuleContext: hookstage.ModuleContext{
- wurflHeaderCtxKey: map[string]string{
+ ModuleContext: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(wurflHeaderCtxKey, map[string]string{
"User-Agent": "Mozilla/5.0",
- },
- },
+ })
+ return ctx
+ }(),
},
payload: []byte(`{"device":{"ua":"Mozilla/5.0", "ext": {"test": 1}}}`),
expectedErr: false,
@@ -328,11 +335,13 @@ func TestHandleRawAuctionHook(t *testing.T) {
extCaps: false,
},
invocationCtx: hookstage.ModuleInvocationContext{
- ModuleContext: hookstage.ModuleContext{
- wurflHeaderCtxKey: map[string]string{
+ ModuleContext: func() *hookstage.ModuleContext {
+ ctx := hookstage.NewModuleContext()
+ ctx.Set(wurflHeaderCtxKey, map[string]string{
"User-Agent": "Mozilla/5.0",
- },
- },
+ })
+ return ctx
+ }(),
},
payload: []byte(`{"device":{"ua":"Mozilla/5.0"}}`),
expectedErr: false,
diff --git a/modules/scope3/rtd/module.go b/modules/scope3/rtd/module.go
index ab4ac3b4595..9ad4e617029 100644
--- a/modules/scope3/rtd/module.go
+++ b/modules/scope3/rtd/module.go
@@ -186,11 +186,11 @@ func (m *Module) HandleEntrypointHook(
miCtx hookstage.ModuleInvocationContext,
payload hookstage.EntrypointPayload,
) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
+ moduleContext := hookstage.NewModuleContext()
+ moduleContext.Set(asyncRequestKey, m.NewAsyncRequest(payload.Request))
// Initialize module context with sync.Map for thread-safe segment storage
return hookstage.HookResult[hookstage.EntrypointPayload]{
- ModuleContext: hookstage.ModuleContext{
- asyncRequestKey: m.NewAsyncRequest(payload.Request),
- },
+ ModuleContext: moduleContext,
}, nil
}
@@ -203,7 +203,23 @@ func (m *Module) HandleProcessedAuctionHook(
var ret hookstage.HookResult[hookstage.ProcessedAuctionRequestPayload]
analyticsNamePrefix := "HandleProcessedAuctionHook."
- asyncRequest, ok := miCtx.ModuleContext[asyncRequestKey].(*AsyncRequest)
+ request, ok := miCtx.ModuleContext.Get(asyncRequestKey)
+ if !ok {
+ // Log error but don't fail the auction
+ ret.AnalyticsTags = hookanalytics.Analytics{
+ Activities: []hookanalytics.Activity{{
+ Name: analyticsNamePrefix + asyncRequestKey,
+ Status: hookanalytics.ActivityStatusError,
+ Results: []hookanalytics.Result{{
+ Status: hookanalytics.ResultStatusError,
+ Values: map[string]interface{}{"error": "failed to get async request from module context"},
+ }},
+ }},
+ }
+ return ret, nil
+ }
+
+ asyncRequest, ok := request.(*AsyncRequest)
if !ok {
// Log error but don't fail the auction
ret.AnalyticsTags = hookanalytics.Analytics{
@@ -233,7 +249,24 @@ func (m *Module) HandleAuctionResponseHook(
) (hookstage.HookResult[hookstage.AuctionResponsePayload], error) {
analyticsNamePrefix := "HandleAuctionResponseHook."
var ret hookstage.HookResult[hookstage.AuctionResponsePayload]
- asyncRequest, ok := miCtx.ModuleContext[asyncRequestKey].(*AsyncRequest)
+
+ request, ok := miCtx.ModuleContext.Get(asyncRequestKey)
+ if !ok {
+ // Log error but don't fail the auction
+ ret.AnalyticsTags = hookanalytics.Analytics{
+ Activities: []hookanalytics.Activity{{
+ Name: analyticsNamePrefix + asyncRequestKey,
+ Status: hookanalytics.ActivityStatusError,
+ Results: []hookanalytics.Result{{
+ Status: hookanalytics.ResultStatusError,
+ Values: map[string]interface{}{"error": "failed to get async request from module context"},
+ }},
+ }},
+ }
+ return ret, nil
+ }
+
+ asyncRequest, ok := request.(*AsyncRequest)
if !ok {
// Log error but don't fail the auction
ret.AnalyticsTags = hookanalytics.Analytics{
diff --git a/modules/scope3/rtd/module_test.go b/modules/scope3/rtd/module_test.go
index a9339d32425..6b40353cf07 100644
--- a/modules/scope3/rtd/module_test.go
+++ b/modules/scope3/rtd/module_test.go
@@ -83,16 +83,19 @@ func TestHandleEntrypointHook(t *testing.T) {
result, err := module.HandleEntrypointHook(ctx, miCtx, payload)
assert.NoError(t, err)
- assert.NotNil(t, result.ModuleContext[asyncRequestKey])
+ asyncRequest, _ := result.ModuleContext.Get(asyncRequestKey)
+ assert.NotNil(t, asyncRequest)
}
func TestHandleAuctionResponseHook_NoSegments(t *testing.T) {
module := &Module{}
ctx := context.Background()
miCtx := hookstage.ModuleInvocationContext{
- ModuleContext: hookstage.ModuleContext{
- "segments": &sync.Map{},
- },
+ ModuleContext: func() *hookstage.ModuleContext {
+ mctx := hookstage.NewModuleContext()
+ mctx.Set("segments", &sync.Map{})
+ return mctx
+ }(),
}
payload := hookstage.AuctionResponsePayload{}
@@ -256,7 +259,8 @@ func TestScope3APIIntegrationNoSegments(t *testing.T) {
// Test entrypoint hook
entrypointResult, err := module.HandleEntrypointHook(ctx, hookstage.ModuleInvocationContext{}, getTestEntrypointPayload(t))
require.NoError(t, err)
- assert.NotNil(t, entrypointResult.ModuleContext[asyncRequestKey])
+ asyncRequest, _ := entrypointResult.ModuleContext.Get(asyncRequestKey)
+ assert.NotNil(t, asyncRequest)
payload := hookstage.ProcessedAuctionRequestPayload{
Request: &openrtb_ext.RequestWrapper{
@@ -359,7 +363,8 @@ func TestScope3APIIntegrationWithTargeting(t *testing.T) {
// Test entrypoint hook
entrypointResult, err := module.HandleEntrypointHook(ctx, hookstage.ModuleInvocationContext{}, getTestEntrypointPayload(t))
require.NoError(t, err)
- assert.NotNil(t, entrypointResult.ModuleContext[asyncRequestKey])
+ asyncRequest, _ := entrypointResult.ModuleContext.Get(asyncRequestKey)
+ assert.NotNil(t, asyncRequest)
// Create test request payload
width := int64(300)
@@ -541,7 +546,8 @@ func TestScope3APIIntegrationWithTargetingNoScope3Section(t *testing.T) {
// Test entrypoint hook
entrypointResult, err := module.HandleEntrypointHook(ctx, hookstage.ModuleInvocationContext{}, getTestEntrypointPayload(t))
require.NoError(t, err)
- assert.NotNil(t, entrypointResult.ModuleContext[asyncRequestKey])
+ asyncRequest, _ := entrypointResult.ModuleContext.Get(asyncRequestKey)
+ assert.NotNil(t, asyncRequest)
// Create test request payload
width := int64(300)
@@ -684,7 +690,8 @@ func TestScope3APIIntegrationWithExistingPrebidTargeting(t *testing.T) {
// Test entrypoint hook
entrypointResult, err := module.HandleEntrypointHook(ctx, hookstage.ModuleInvocationContext{}, getTestEntrypointPayload(t))
require.NoError(t, err)
- assert.NotNil(t, entrypointResult.ModuleContext[asyncRequestKey])
+ asyncRequest, _ := entrypointResult.ModuleContext.Get(asyncRequestKey)
+ assert.NotNil(t, asyncRequest)
// Create test request payload
width := int64(300)
@@ -794,7 +801,8 @@ func TestScope3APIIntegrationWithExistingPrebidNoTargeting(t *testing.T) {
// Test entrypoint hook
entrypointResult, err := module.HandleEntrypointHook(ctx, hookstage.ModuleInvocationContext{}, getTestEntrypointPayload(t))
require.NoError(t, err)
- assert.NotNil(t, entrypointResult.ModuleContext[asyncRequestKey])
+ asyncRequest, _ := entrypointResult.ModuleContext.Get(asyncRequestKey)
+ assert.NotNil(t, asyncRequest)
// Create test request payload
width := int64(300)