Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
b08a06c
Add Boldwin-Rapid adapter with tests and configuration files
BoldwinDev Aug 8, 2025
25e8722
Update method parameters in `boldwin_rapid` adapter to discard unused…
BoldwinDev Aug 8, 2025
01671a1
Merge branch 'master' into boldwin-rapid-adapter
BoldwinDev Aug 8, 2025
472ba65
Update HTTP headers in `boldwin_rapid` adapter to include `IP` and fi…
BoldwinDev Aug 8, 2025
950cccd
Simplify error handling in `MakeRequests` method of `boldwin_rapid` a…
BoldwinDev Aug 8, 2025
1536181
Add `Ip` field to exemplary JSON files in `boldwin_rapid` tests
BoldwinDev Aug 8, 2025
236d9f5
Add additional unit tests for error handling in `MakeRequests` and he…
BoldwinDev Aug 8, 2025
9960e04
Add unit test for direct `buildEndpointURL` error handling in `boldwi…
BoldwinDev Aug 8, 2025
20f1aa9
Merge branch 'prebid:master' into boldwin-rapid-adapter
BoldwinDev Sep 11, 2025
c559040
Changes were made to the code based on comments from SyntaxNode
BoldwinDev Sep 11, 2025
3fac16e
Merge branch 'prebid:master' into boldwin-rapid-adapter
BoldwinDev Sep 18, 2025
e4792b5
Exclude `boldwin_rapid` from core bidder validation in unit tests bas…
BoldwinDev Sep 19, 2025
72e8afb
Merge branch 'master' into boldwin-rapid-adapter
BoldwinDev Oct 9, 2025
b9f1984
Added empty-check for Currency field
BoldwinDev Oct 9, 2025
729e1c1
Added openrtb version to boldwin_rapid.yaml; Added several changes to…
BoldwinDev Oct 17, 2025
38a38c3
Move getHeaders() call outside the impression loop to avoid creating
BoldwinDev Oct 29, 2025
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
172 changes: 172 additions & 0 deletions adapters/boldwin_rapid/boldwin_rapid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package boldwin_rapid

import (
"encoding/json"
"fmt"
"text/template"

"net/http"

"github.com/prebid/openrtb/v20/openrtb2"
"github.com/prebid/prebid-server/v3/adapters"
"github.com/prebid/prebid-server/v3/config"
"github.com/prebid/prebid-server/v3/macros"
"github.com/prebid/prebid-server/v3/openrtb_ext"
"github.com/prebid/prebid-server/v3/util/jsonutil"
)

type adapter struct {
endpoint *template.Template
}

func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) {
endpointTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint)
if err != nil {
return nil, fmt.Errorf("unable to parse endpoint template: %v", err)
}

bidder := &adapter{
endpoint: endpointTemplate,
}

return bidder, nil
}

func (a *adapter) buildEndpointURL(boldwinExt openrtb_ext.ImpExtBoldwinRapid) (string, error) {
endpointParams := macros.EndpointTemplateParams{
PublisherID: boldwinExt.Pid,
PlacementID: boldwinExt.Tid,
}

return macros.ResolveMacros(a.endpoint, endpointParams)
}

func (a *adapter) MakeRequests(request *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
var adapterRequests []*adapters.RequestData

reqCopy := *request
headers := a.getHeaders(request)

for _, imp := range request.Imp {
// Create a new request with just this impression
reqCopy.Imp = []openrtb2.Imp{imp}
Comment thread
BoldwinDev marked this conversation as resolved.

var bidderExt adapters.ExtImpBidder
var boldwinExt openrtb_ext.ImpExtBoldwinRapid

// Use the current impression's Ext
if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, []error{err}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you please add test coverage for these unmarshal errors

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests added

}

if err := jsonutil.Unmarshal(bidderExt.Bidder, &boldwinExt); err != nil {
return nil, []error{err}
}

endpoint, err := a.buildEndpointURL(boldwinExt)
if err != nil {
return nil, []error{err}
}

adapterReq, err := a.makeRequest(&reqCopy, endpoint, headers)
if err != nil {
return nil, []error{err}
}

if adapterReq != nil {
adapterRequests = append(adapterRequests, adapterReq)
}
}

return adapterRequests, nil
}

func (a *adapter) getHeaders(request *openrtb2.BidRequest) http.Header {
headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")
headers.Add("x-openrtb-version", "2.5")

@ccorbo ccorbo Oct 16, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you support openrtb version 2.5, can you please declare openrtb version 2.5 in your .yaml file. Example here under bidder info: https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added version indication to the yaml file
openrtb: version: 2.5

headers.Add("Host", "rtb.beardfleet.com") // required header for the request
Comment thread
BoldwinDev marked this conversation as resolved.

if request.Device != nil {
if request.Device.UA != "" {
headers.Add("User-Agent", request.Device.UA)
}

if len(request.Device.IPv6) > 0 {
headers.Set("X-Forwarded-For", request.Device.IPv6)
}

if len(request.Device.IP) > 0 {
headers.Set("X-Forwarded-For", request.Device.IP)
headers.Add("IP", request.Device.IP)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the edge case where both request.Device.IP and request.Device.IPv6 are non-empty, this logic prioritizes request.Device.IP over request.Device.IPv6. Is this intended? Or should it be the other way around?

 90     if request.Device != nil {
 91         if request.Device.UA != "" {
 92             headers.Add("User-Agent", request.Device.UA)
 93         }
 94 -
 95 -       if len(request.Device.IPv6) > 0 {
 96 -           headers.Add("X-Forwarded-For", request.Device.IPv6)
 97 -       }
 98
 99         if len(request.Device.IP) > 0 {
100             headers.Add("X-Forwarded-For", request.Device.IP)
101             headers.Add("IP", request.Device.IP)
102         }
    +
    +       if len(request.Device.IPv6) > 0 {
    +           headers.Add("X-Forwarded-For", request.Device.IPv6)
    +       }
103     }
adapters/boldwin_rapid/boldwin_rapid.go

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching it, It's better for us to prioritize the request.Device.IP over request.Device.IPv6

}
}

return headers
}

func (a *adapter) makeRequest(request *openrtb2.BidRequest, endpoint string, headers http.Header) (*adapters.RequestData, error) {
reqJSON, err := json.Marshal(request)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}

return &adapters.RequestData{
Method: "POST",
Uri: endpoint,
Body: reqJSON,
Headers: headers,
ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
}, nil
}

func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
if adapters.IsResponseStatusCodeNoContent(responseData) {
return nil, nil
}

err := adapters.CheckResponseStatusCodeForErrors(responseData)
if err != nil {
return nil, []error{err}
}

var response openrtb2.BidResponse
if err := jsonutil.Unmarshal(responseData.Body, &response); err != nil {
return nil, []error{err}
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
if response.Cur != "" {
bidResponse.Currency = response.Cur
}

for _, seatBid := range response.SeatBid {
for i := range seatBid.Bid {
bidType, err := getBidMediaType(&seatBid.Bid[i])
if err != nil {
return nil, []error{err}
}

b := &adapters.TypedBid{
Bid: &seatBid.Bid[i],
BidType: bidType,
}
bidResponse.Bids = append(bidResponse.Bids, b)
}
}
return bidResponse, nil
}

func getBidMediaType(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
switch bid.MType {
case openrtb2.MarkupBanner:
return openrtb_ext.BidTypeBanner, nil
case openrtb2.MarkupVideo:
return openrtb_ext.BidTypeVideo, nil
case openrtb2.MarkupNative:
return openrtb_ext.BidTypeNative, nil
default:
return "", fmt.Errorf("Unable to fetch mediaType in multi-format: %s", bid.ImpID)
}
}
Loading
Loading