Skip to content

Commit

Permalink
Debug override header (prebid#1853)
Browse files Browse the repository at this point in the history
  • Loading branch information
VeronikaSolovei9 authored and sachin-pubmatic committed Aug 2, 2021
1 parent 07e3fa4 commit b0230ea
Show file tree
Hide file tree
Showing 15 changed files with 186 additions and 71 deletions.
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ type DefReqFiles struct {

type Debug struct {
TimeoutNotification TimeoutNotification `mapstructure:"timeout_notification"`
OverrideToken string `mapstructure:"override_token"`
}

func (cfg *Debug) validate(errs []error) []error {
Expand Down Expand Up @@ -1000,6 +1001,7 @@ func SetupViper(v *viper.Viper, filename string) {
v.SetDefault("debug.timeout_notification.log", false)
v.SetDefault("debug.timeout_notification.sampling_rate", 0.0)
v.SetDefault("debug.timeout_notification.fail_only", false)
v.SetDefault("debug.override_token", "")

/* IPv4
/* Site Local: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
Expand Down
1 change: 1 addition & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ func TestFullConfig(t *testing.T) {
cmpStrings(t, "request_validation.ipv6_private_networks", cfg.RequestValidation.IPv6PrivateNetworks[0], "1111::/16")
cmpStrings(t, "request_validation.ipv6_private_networks", cfg.RequestValidation.IPv6PrivateNetworks[1], "2222::/16")
cmpBools(t, "generate_bid_id", cfg.GenerateBidID, true)
cmpStrings(t, "debug.override_token", cfg.Debug.OverrideToken, "")
}

func TestUnmarshalAdapterExtraInfo(t *testing.T) {
Expand Down
18 changes: 10 additions & 8 deletions endpoints/openrtb2/video_auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,13 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
cacheTTL = int64(deps.cfg.CacheURL.DefaultTTLs.Video)
}
debugLog := exchange.DebugLog{
Enabled: strings.EqualFold(debugQuery, "true"),
CacheType: prebid_cache_client.TypeXML,
TTL: cacheTTL,
Regexp: deps.debugLogRegexp,
Enabled: strings.EqualFold(debugQuery, "true"),
CacheType: prebid_cache_client.TypeXML,
TTL: cacheTTL,
Regexp: deps.debugLogRegexp,
DebugOverride: exchange.IsDebugOverrideEnabled(r.Header.Get(exchange.DebugOverrideHeader), deps.cfg.Debug.OverrideToken),
}
debugLog.DebugEnabledOrOverridden = debugLog.Enabled || debugLog.DebugOverride

defer func() {
if len(debugLog.CacheKey) > 0 && vo.VideoResponse == nil {
Expand All @@ -157,7 +159,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
}

resolvedRequest := requestJson
if debugLog.Enabled {
if debugLog.DebugEnabledOrOverridden {
debugLog.Data.Request = string(requestJson)
if headerBytes, err := json.Marshal(r.Header); err == nil {
debugLog.Data.Headers = string(headerBytes)
Expand Down Expand Up @@ -209,7 +211,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
//create full open rtb req from full video request
mergeData(videoBidReq, bidReq)
// If debug query param is set, force the response to enable test flag
if debugLog.Enabled {
if debugLog.DebugEnabledOrOverridden {
bidReq.Test = 1
}

Expand Down Expand Up @@ -306,7 +308,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
bidResp.Ext = response.Ext
}

if len(bidResp.AdPods) == 0 && debugLog.Enabled {
if len(bidResp.AdPods) == 0 && debugLog.DebugEnabledOrOverridden {
err := debugLog.PutDebugLogError(deps.cache, deps.cfg.CacheURL.ExpectedTimeMillis, vo.Errors)
if err != nil {
vo.Errors = append(vo.Errors, err)
Expand Down Expand Up @@ -344,7 +346,7 @@ func cleanupVideoBidRequest(videoReq *openrtb_ext.BidRequestVideo, podErrors []P
}

func handleError(labels *metrics.Labels, w http.ResponseWriter, errL []error, vo *analytics.VideoObject, debugLog *exchange.DebugLog) {
if debugLog != nil && debugLog.Enabled {
if debugLog != nil && debugLog.DebugEnabledOrOverridden {
if rawUUID, err := uuid.NewV4(); err == nil {
debugLog.CacheKey = rawUUID.String()
}
Expand Down
6 changes: 4 additions & 2 deletions endpoints/openrtb2/video_auction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1041,8 +1041,10 @@ func TestHandleErrorDebugLog(t *testing.T) {
Headers: "test headers string",
Response: "test response string",
},
TTL: int64(3600),
Regexp: regexp.MustCompile(`[<>]`),
TTL: int64(3600),
Regexp: regexp.MustCompile(`[<>]`),
DebugOverride: false,
DebugEnabledOrOverridden: true,
}
handleError(&labels, recorder, []error{err1, err2}, &vo, &debugLog)

Expand Down
27 changes: 19 additions & 8 deletions exchange/auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,21 @@ import (
"github.com/prebid/prebid-server/prebid_cache_client"
)

const (
DebugOverrideHeader string = "x-pbs-debug-override"
)

type DebugLog struct {
Enabled bool
CacheType prebid_cache_client.PayloadType
Data DebugData
TTL int64
CacheKey string
CacheString string
Regexp *regexp.Regexp
Enabled bool
CacheType prebid_cache_client.PayloadType
Data DebugData
TTL int64
CacheKey string
CacheString string
Regexp *regexp.Regexp
DebugOverride bool
//little optimization, it stores value of debugLog.Enabled || debugLog.DebugOverride
DebugEnabledOrOverridden bool
}

type DebugData struct {
Expand All @@ -47,6 +54,10 @@ func (d *DebugLog) BuildCacheString() {
d.CacheString = fmt.Sprintf("%s<Log>%s%s%s</Log>", xml.Header, d.Data.Request, d.Data.Headers, d.Data.Response)
}

func IsDebugOverrideEnabled(debugHeader, configOverrideToken string) bool {
return configOverrideToken != "" && debugHeader == configOverrideToken
}

func (d *DebugLog) PutDebugLogError(cache prebid_cache_client.Client, timeout int, errors []error) error {
if len(d.Data.Response) == 0 && len(errors) == 0 {
d.Data.Response = "No response or errors created"
Expand Down Expand Up @@ -238,7 +249,7 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client,
}
}

if len(toCache) > 0 && debugLog != nil && debugLog.Enabled {
if len(toCache) > 0 && debugLog != nil && debugLog.DebugEnabledOrOverridden {
debugLog.CacheKey = hbCacheID
debugLog.BuildCacheString()
if jsonBytes, err := json.Marshal(debugLog.CacheString); err == nil {
Expand Down
50 changes: 50 additions & 0 deletions exchange/auction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,56 @@ func TestCacheJSON(t *testing.T) {
}
}

func TestIsDebugOverrideEnabled(t *testing.T) {
type inTest struct {
debugHeader string
configToken string
}
type aTest struct {
desc string
in inTest
result bool
}
testCases := []aTest{
{
desc: "test debug header is empty, config token is empty",
in: inTest{debugHeader: "", configToken: ""},
result: false,
},
{
desc: "test debug header is present, config token is empty",
in: inTest{debugHeader: "TestToken", configToken: ""},
result: false,
},
{
desc: "test debug header is empty, config token is present",
in: inTest{debugHeader: "", configToken: "TestToken"},
result: false,
},
{
desc: "test debug header is present, config token is present, not equal",
in: inTest{debugHeader: "TestToken123", configToken: "TestToken"},
result: false,
},
{
desc: "test debug header is present, config token is present, equal",
in: inTest{debugHeader: "TestToken", configToken: "TestToken"},
result: true,
},
{
desc: "test debug header is present, config token is present, not case equal",
in: inTest{debugHeader: "TestTokeN", configToken: "TestToken"},
result: false,
},
}

for _, test := range testCases {
result := IsDebugOverrideEnabled(test.in.debugHeader, test.in.configToken)
assert.Equal(t, test.result, result, test.desc)
}

}

// LoadCacheSpec reads and parses a file as a test case. If something goes wrong, it returns an error.
func loadCacheSpec(filename string) (*cacheSpec, error) {
specData, err := ioutil.ReadFile(filename)
Expand Down
28 changes: 17 additions & 11 deletions exchange/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type adaptedBidder interface {
//
// Any errors will be user-facing in the API.
// Error messages should help publishers understand what might account for "bad" bids.
requestBid(ctx context.Context, request *openrtb2.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error)
requestBid(ctx context.Context, request *openrtb2.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) (*pbsOrtbSeatBid, []error)
}

// pbsOrtbBid is a Bid returned by an adaptedBidder.
Expand Down Expand Up @@ -128,7 +128,7 @@ type bidderAdapterConfig struct {
DebugInfo config.DebugInfo
}

func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb2.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb2.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
reqData, errs := bidder.Bidder.MakeRequests(request, reqInfo)

if len(reqData) == 0 {
Expand Down Expand Up @@ -178,19 +178,25 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, request *openrtb2.B
httpInfo := <-responseChannel
// If this is a test bid, capture debugging info from the requests.
// Write debug data to ext in case if:
// - headerDebugAllowed (debug override header specified correct) - it overrides all other debug restrictions
// - debugContextKey (url param) in true
// - account debug is allowed
// - bidder debug is allowed
if debugInfo := ctx.Value(DebugContextKey); debugInfo != nil && debugInfo.(bool) {
if accountDebugAllowed {
if bidder.config.DebugInfo.Allow {
seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
} else {
debugDisabledWarning := errortypes.Warning{
WarningCode: errortypes.BidderLevelDebugDisabledWarningCode,
Message: "debug turned off for bidder",
if headerDebugAllowed {
seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
} else {
debugInfo := ctx.Value(DebugContextKey)
if debugInfo != nil && debugInfo.(bool) {
if accountDebugAllowed {
if bidder.config.DebugInfo.Allow {
seatBid.httpCalls = append(seatBid.httpCalls, makeExt(httpInfo))
} else {
debugDisabledWarning := errortypes.Warning{
WarningCode: errortypes.BidderLevelDebugDisabledWarningCode,
Message: "debug turned off for bidder",
}
errs = append(errs, &debugDisabledWarning)
}
errs = append(errs, &debugDisabledWarning)
}
}
}
Expand Down
18 changes: 11 additions & 7 deletions exchange/bidder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func TestSingleBidder(t *testing.T) {
bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, test.debugInfo)
currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))

seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true, false)

// Make sure the goodSingleBidder was called with the expected arguments.
if bidderImpl.httpResponse == nil {
Expand Down Expand Up @@ -167,7 +167,7 @@ func TestRequestBidRemovesSensitiveHeaders(t *testing.T) {

bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, debugInfo)
currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true, false)

expectedHttpCalls := []*openrtb_ext.ExtHttpCall{
{
Expand Down Expand Up @@ -208,7 +208,7 @@ func TestSetGPCHeader(t *testing.T) {

bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, debugInfo)
currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{GlobalPrivacyControlHeader: "1"}, true)
seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{GlobalPrivacyControlHeader: "1"}, true, false)

expectedHttpCall := []*openrtb_ext.ExtHttpCall{
{
Expand Down Expand Up @@ -246,7 +246,7 @@ func TestSetGPCHeaderNil(t *testing.T) {

bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, debugInfo)
currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{GlobalPrivacyControlHeader: "1"}, true)
seatBid, errs := bidder.requestBid(ctx, &openrtb2.BidRequest{}, "test", 1, currencyConverter.Rates(), &adapters.ExtraRequestInfo{GlobalPrivacyControlHeader: "1"}, true, false)

expectedHttpCall := []*openrtb_ext.ExtHttpCall{
{
Expand Down Expand Up @@ -304,7 +304,7 @@ func TestMultiBidder(t *testing.T) {
}
bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil)
currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
seatBid, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
seatBid, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true, true)

if seatBid == nil {
t.Fatalf("SeatBid should exist, because bids exist.")
Expand Down Expand Up @@ -681,6 +681,7 @@ func TestMultiCurrencies(t *testing.T) {
currencyConverter.Rates(),
&adapters.ExtraRequestInfo{},
true,
true,
)

// Verify:
Expand Down Expand Up @@ -826,6 +827,7 @@ func TestMultiCurrencies_RateConverterNotSet(t *testing.T) {
currencyConverter.Rates(),
&adapters.ExtraRequestInfo{},
true,
true,
)

// Verify:
Expand Down Expand Up @@ -999,6 +1001,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) {
currencyConverter.Rates(),
&adapters.ExtraRequestInfo{},
true,
false,
)

// Verify:
Expand Down Expand Up @@ -1303,6 +1306,7 @@ func TestMobileNativeTypes(t *testing.T) {
currencyConverter.Rates(),
&adapters.ExtraRequestInfo{},
true,
true,
)

var actualValue string
Expand All @@ -1316,7 +1320,7 @@ func TestMobileNativeTypes(t *testing.T) {
func TestErrorReporting(t *testing.T) {
bidder := adaptBidder(&bidRejector{}, nil, &config.Configuration{}, &metricsConfig.DummyMetricsEngine{}, openrtb_ext.BidderAppnexus, nil)
currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
bids, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
bids, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, "test", 1.0, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true, false)
if bids != nil {
t.Errorf("There should be no seatbid if no http requests are returned.")
}
Expand Down Expand Up @@ -1537,7 +1541,7 @@ func TestCallRecordAdapterConnections(t *testing.T) {
// Run requestBid using an http.Client with a mock handler
bidder := adaptBidder(bidderImpl, server.Client(), &config.Configuration{}, metrics, openrtb_ext.BidderAppnexus, nil)
currencyConverter := currency.NewRateConverter(&http.Client{}, "", time.Duration(0))
_, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true)
_, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, "test", bidAdjustment, currencyConverter.Rates(), &adapters.ExtraRequestInfo{}, true, true)

// Assert no errors
assert.Equal(t, 0, len(errs), "bidder.requestBid returned errors %v \n", errs)
Expand Down
4 changes: 2 additions & 2 deletions exchange/bidder_validate_bids.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ type validatedBidder struct {
bidder adaptedBidder
}

func (v *validatedBidder) requestBid(ctx context.Context, request *openrtb2.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
seatBid, errs := v.bidder.requestBid(ctx, request, name, bidAdjustment, conversions, reqInfo, accountDebugAllowed)
func (v *validatedBidder) requestBid(ctx context.Context, request *openrtb2.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
seatBid, errs := v.bidder.requestBid(ctx, request, name, bidAdjustment, conversions, reqInfo, accountDebugAllowed, headerDebugAllowed)
if validationErrors := removeInvalidBids(request, seatBid); len(validationErrors) > 0 {
errs = append(errs, validationErrors...)
}
Expand Down
10 changes: 5 additions & 5 deletions exchange/bidder_validate_bids_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestAllValidBids(t *testing.T) {
},
},
})
seatBid, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true)
seatBid, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true, false)
assert.Len(t, seatBid.bids, 3)
assert.Len(t, errs, 0)
}
Expand Down Expand Up @@ -83,7 +83,7 @@ func TestAllBadBids(t *testing.T) {
},
},
})
seatBid, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true)
seatBid, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true, false)
assert.Len(t, seatBid.bids, 0)
assert.Len(t, errs, 5)
}
Expand Down Expand Up @@ -126,7 +126,7 @@ func TestMixedBids(t *testing.T) {
},
},
})
seatBid, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true)
seatBid, errs := bidder.requestBid(context.Background(), &openrtb2.BidRequest{}, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true, false)
assert.Len(t, seatBid.bids, 2)
assert.Len(t, errs, 3)
}
Expand Down Expand Up @@ -246,7 +246,7 @@ func TestCurrencyBids(t *testing.T) {
Cur: tc.brqCur,
}

seatBid, errs := bidder.requestBid(context.Background(), request, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true)
seatBid, errs := bidder.requestBid(context.Background(), request, openrtb_ext.BidderAppnexus, 1.0, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, true, false)
assert.Len(t, seatBid.bids, expectedValidBids)
assert.Len(t, errs, expectedErrs)
}
Expand All @@ -257,6 +257,6 @@ type mockAdaptedBidder struct {
errorResponse []error
}

func (b *mockAdaptedBidder) requestBid(ctx context.Context, request *openrtb2.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
func (b *mockAdaptedBidder) requestBid(ctx context.Context, request *openrtb2.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, accountDebugAllowed, headerDebugAllowed bool) (*pbsOrtbSeatBid, []error) {
return b.bidResponse, b.errorResponse
}
Loading

0 comments on commit b0230ea

Please sign in to comment.