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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions adapters/afront/afront.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package afront

import (
"fmt"
"net/http"
"text/template"

"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/errortypes"
"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(
bidderName openrtb_ext.BidderName,
config config.Adapter,
server config.Server,
) (
adapters.Bidder,
error,
) {
template, err := template.New("endpointTemplate").Parse(config.Endpoint)
if err != nil {
return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
}

bidder := &adapter{
endpoint: template,
}
return bidder, nil
}

func getHeaders(request *openrtb2.BidRequest) http.Header {
headers := http.Header{}
headers.Add("X-Openrtb-Version", "2.5")
headers.Add("Accept", "application/json")
headers.Add("Content-Type", "application/json;charset=utf-8")

if request.Device != nil {
if len(request.Device.IP) > 0 {
headers.Add("X-Forwarded-For", request.Device.IP)
}
if len(request.Device.IPv6) > 0 {
headers.Add("X-Forwarded-For", request.Device.IPv6)
}
if len(request.Device.UA) > 0 {
headers.Add("User-Agent", request.Device.UA)
}
}

return headers
}

func (a *adapter) MakeRequests(
openRTBRequest *openrtb2.BidRequest,
reqInfo *adapters.ExtraRequestInfo,
) (
requestsToBidder []*adapters.RequestData,
errs []error,
) {
afrontExt, err := a.getImpressionExt(&openRTBRequest.Imp[0])
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.

Unsafe array access: kindly add non-empty check before accessing Imp[0] for safer side.

Copy link
Copy Markdown
Contributor

@VeronikaSolovei9 VeronikaSolovei9 Jul 17, 2025

Choose a reason for hiding this comment

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

Just realized I also commented on this: #4380 (comment)

if err != nil {
return nil, []error{err}
}

url, err := a.buildEndpointURL(afrontExt)
Comment thread
Afrontio marked this conversation as resolved.
if err != nil {
return nil, []error{err}
}

for idx := range openRTBRequest.Imp {
openRTBRequest.Imp[idx].Ext = nil
}

reqJSON, err := jsonutil.Marshal(openRTBRequest)
if err != nil {
return nil, []error{err}
}

return []*adapters.RequestData{{
Method: http.MethodPost,
Body: reqJSON,
Uri: url,
Headers: getHeaders(openRTBRequest),
ImpIDs: openrtb_ext.GetImpIDs(openRTBRequest.Imp),
}}, nil
}

func (a *adapter) getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtAfront, error) {
var bidderExt adapters.ExtImpBidder
if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, &errortypes.BadInput{
Message: "ext.bidder not provided",
}
}
var afrontExt openrtb_ext.ExtAfront
if err := jsonutil.Unmarshal(bidderExt.Bidder, &afrontExt); err != nil {
return nil, &errortypes.BadInput{
Message: "ext.bidder not provided",
}
}

return &afrontExt, nil
}

func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtAfront) (string, error) {
endpointParams := macros.EndpointTemplateParams{
AccountID: params.AccountID,
SourceId: params.SourceId,
}
return macros.ResolveMacros(a.endpoint, endpointParams)
}

func (a *adapter) MakeBids(
openRTBRequest *openrtb2.BidRequest,
requestToBidder *adapters.RequestData,
bidderRawResponse *adapters.ResponseData,
) (
bidderResponse *adapters.BidderResponse,
errs []error,
) {
if adapters.IsResponseStatusCodeNoContent(bidderRawResponse) {
return nil, nil
}

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

var bidResp openrtb2.BidResponse
if err := jsonutil.Unmarshal(bidderRawResponse.Body, &bidResp); err != nil {
return nil, []error{&errortypes.BadServerResponse{
Message: "Bad Server Response",
}}
}

if len(bidResp.SeatBid) == 0 {
return nil, []error{&errortypes.BadServerResponse{
Message: "Empty SeatBid array",
}}
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
var bidsArray []*adapters.TypedBid

for idx, bid := range bidResp.SeatBid[0].Bid {
bidsArray = append(bidsArray, &adapters.TypedBid{
Bid: &bidResp.SeatBid[0].Bid[idx],
BidType: getMediaTypeForImp(bid.ImpID, openRTBRequest.Imp),
})
}

bidResponse.Bids = bidsArray
return bidResponse, nil
}

func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
mediaType := openrtb_ext.BidTypeBanner
for _, imp := range imps {
if imp.ID == impId {
if imp.Video != nil {
mediaType = openrtb_ext.BidTypeVideo
} else if imp.Native != nil {
mediaType = openrtb_ext.BidTypeNative
}
return mediaType
}
}
return mediaType
}
29 changes: 29 additions & 0 deletions adapters/afront/afront_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package afront

import (
"testing"

"github.com/prebid/prebid-server/v3/adapters/adapterstest"
"github.com/prebid/prebid-server/v3/config"
"github.com/prebid/prebid-server/v3/openrtb_ext"
"github.com/stretchr/testify/assert"
)

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder(openrtb_ext.BidderAfront, config.Adapter{
Endpoint: "https://snt1.afront.io/?rsd={{.SourceId}}&sk={{.AccountID}}",
}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})

if buildErr != nil {
t.Fatalf("Builder returned unexpected error %v", buildErr)
}

adapterstest.RunJSONBidderTest(t, "afronttest", bidder)
}

func TestEndpointTemplateMalformed(t *testing.T) {
_, buildErr := Builder(openrtb_ext.BidderAfront, config.Adapter{
Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})

assert.Error(t, buildErr)
}
153 changes: 153 additions & 0 deletions adapters/afront/afronttest/exemplary/banner-app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{
"mockBidRequest": {
"id": "req-id",
"device": {
"ua": "test-user-agent",
"ip": "0.0.0.0",
"language": "en",
"dnt": 0
},
"tmax": 1000,
"user": {
"buyeruid": "test-user"
},
"app": {
"publisher": {
"id": "3998392"
},
"cat": [
"IAB22-1"
],
"bundle": "com.app.awesome",
"name": "Awesome App",
"domain": "awesomeapp.com",
"id": "3998392"
},
"imp": [
{
"id": "some-impression-id",
"tagid": "ogTAGID",
"banner": {
"w": 320,
"h": 50
},
"ext": {
"bidder": {
"accountId": "accountId",
"sourceId": "sourceId"
}
}
}
]
},
"httpCalls": [
{
"expectedRequest": {
"headers": {
"Content-Type": [
"application/json;charset=utf-8"
],
"Accept": [
"application/json"
],
"X-Openrtb-Version": [
"2.5"
],
"User-Agent": [
"test-user-agent"
],
"X-Forwarded-For": [
"0.0.0.0"
]
},
"uri": "https://snt1.afront.io/?rsd=sourceId&sk=accountId",
"body": {
"id": "req-id",
"device": {
"ua": "test-user-agent",
"ip": "0.0.0.0",
"language": "en",
"dnt": 0
},
"imp": [
{
"id": "some-impression-id",
"banner": {
"w": 320,
"h": 50
},
"tagid": "ogTAGID"
}
],
"app": {
"id": "3998392",
"name": "Awesome App",
"bundle": "com.app.awesome",
"domain": "awesomeapp.com",
"cat": [
"IAB22-1"
],
"publisher": {
"id": "3998392"
}
},
"user": {
"buyeruid": "test-user"
},
"tmax": 1000
},
"impIDs": [
"some-impression-id"
]
},
"mockResponse": {
"status": 200,
"body": {
"id": "awesome-resp-id",
"seatbid": [
{
"bid": [
{
"id": "a3ae1b4e2fc24a4fb45540082e98e161",
"impid": "1",
"price": 3.5,
"adm": "awesome-markup",
"adomain": [
"awesome.com"
],
"crid": "20",
"w": 320,
"h": 50
}
],
"type": "banner",
"seat": "afront"
}
],
"cur": "USD"
}
}
}
],
"expectedBidResponses": [
{
"bids": [
{
"bid": {
"id": "a3ae1b4e2fc24a4fb45540082e98e161",
"impid": "1",
"price": 3.5,
"adm": "awesome-markup",
"adomain": [
"awesome.com"
],
"crid": "20",
"w": 320,
"h": 50
},
"type": "banner"
}
]
}
]
}
Loading
Loading