diff --git a/adapters/afront/afront.go b/adapters/afront/afront.go new file mode 100644 index 00000000000..f9c41b6a1b5 --- /dev/null +++ b/adapters/afront/afront.go @@ -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]) + if err != nil { + return nil, []error{err} + } + + url, err := a.buildEndpointURL(afrontExt) + 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 +} diff --git a/adapters/afront/afront_test.go b/adapters/afront/afront_test.go new file mode 100644 index 00000000000..8a2fde9523e --- /dev/null +++ b/adapters/afront/afront_test.go @@ -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) +} diff --git a/adapters/afront/afronttest/exemplary/banner-app.json b/adapters/afront/afronttest/exemplary/banner-app.json new file mode 100644 index 00000000000..f0f296b2db0 --- /dev/null +++ b/adapters/afront/afronttest/exemplary/banner-app.json @@ -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" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/exemplary/banner-web.json b/adapters/afront/afronttest/exemplary/banner-web.json new file mode 100644 index 00000000000..60e74c7e6fd --- /dev/null +++ b/adapters/afront/afronttest/exemplary/banner-web.json @@ -0,0 +1,191 @@ +{ + "mockBidRequest": { + "id": "req-id", + "device": { + "ua": "test-user-agent", + "ip": "0.0.0.0", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "test-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "imp": [ + { + "id": "some-impression-id1", + "tagid": "ogTAGID", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "accountId": "accountId", + "sourceId": "sourceId" + } + } + }, + { + "id": "some-impression-id2", + "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-id1", + "tagid": "ogTAGID", + "banner": { + "w": 320, + "h": 50 + } + }, + { + "id": "some-impression-id2", + "tagid": "ogTAGID", + "banner": { + "w": 320, + "h": 50 + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "user": { + "buyeruid": "test-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "some-impression-id1", + "some-impression-id2" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id1", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 320, + "h": 50 + }, + { + "id": "a3ae1b4e2fc24a4fb45540082e98e162", + "impid": "some-impression-id2", + "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": "some-impression-id1", + "price": 3.5, + "adm": "awesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ], + "w": 320, + "h": 50 + }, + "type": "banner" + }, + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e162", + "impid": "some-impression-id2", + "price": 3.5, + "adm": "awesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ], + "w": 320, + "h": 50 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/exemplary/default-host-param.json b/adapters/afront/afronttest/exemplary/default-host-param.json new file mode 100644 index 00000000000..f0f296b2db0 --- /dev/null +++ b/adapters/afront/afronttest/exemplary/default-host-param.json @@ -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" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/exemplary/native-app.json b/adapters/afront/afronttest/exemplary/native-app.json new file mode 100644 index 00000000000..183df3f1a3f --- /dev/null +++ b/adapters/afront/afronttest/exemplary/native-app.json @@ -0,0 +1,149 @@ +{ + "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", + "native": { + "ver": "1.1", + "request": "{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"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", + "native": { + "ver": "1.1", + "request": "{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"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": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20" + } + ], + "type": "native", + "seat": "afront" + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ] + }, + "type": "native" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/exemplary/native-web.json b/adapters/afront/afronttest/exemplary/native-web.json new file mode 100644 index 00000000000..d50816028bd --- /dev/null +++ b/adapters/afront/afronttest/exemplary/native-web.json @@ -0,0 +1,136 @@ +{ + "mockBidRequest": { + "id": "req-id", + "device": { + "ua": "test-user-agent", + "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "test-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "native": { + "ver": "1.1", + "request": "{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"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": [ + "2607:fb90:f27:4512:d800:cb23:a603:e245" + ] + }, + "uri": "https://snt1.afront.io/?rsd=sourceId&sk=accountId", + "body": { + "id": "req-id", + "device": { + "ua": "test-user-agent", + "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "native": { + "ver": "1.1", + "request": "{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}" + } + } + ], + "site": { + "page": "test.com", + "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": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20" + } + ], + "seat": "afront" + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ] + }, + "type": "native" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/exemplary/video-app.json b/adapters/afront/afronttest/exemplary/video-app.json new file mode 100644 index 00000000000..09cc7f04f76 --- /dev/null +++ b/adapters/afront/afronttest/exemplary/video-app.json @@ -0,0 +1,162 @@ +{ + "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", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "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", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "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": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720 + } + ], + "seat": "afront" + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ], + "w": 1280, + "h": 720 + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/exemplary/video-web.json b/adapters/afront/afronttest/exemplary/video-web.json new file mode 100644 index 00000000000..a57ce878c65 --- /dev/null +++ b/adapters/afront/afronttest/exemplary/video-web.json @@ -0,0 +1,160 @@ +{ + "mockBidRequest": { + "id": "req-id", + "device": { + "ua": "test-user-agent", + "ip": "0.0.0.0", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "test-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "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", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + } + } + ], + "site": { + "page": "test.com", + "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": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } + } + } + ], + "seat": "afront" + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/supplemental/empty-seatbid-array.json b/adapters/afront/afronttest/supplemental/empty-seatbid-array.json new file mode 100644 index 00000000000..127c04a5b23 --- /dev/null +++ b/adapters/afront/afronttest/supplemental/empty-seatbid-array.json @@ -0,0 +1,133 @@ +{ + "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", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "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", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "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": [], + "cur": "USD" + } + } + } + ], + "mockResponse": { + "status": 200, + "body": "invalid response" + }, + "expectedMakeBidsErrors": [ + { + "value": "Empty SeatBid array", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/supplemental/invalid-afront-ext-object.json b/adapters/afront/afronttest/supplemental/invalid-afront-ext-object.json new file mode 100644 index 00000000000..77752d01edf --- /dev/null +++ b/adapters/afront/afronttest/supplemental/invalid-afront-ext-object.json @@ -0,0 +1,29 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "ext.bidder not provided", + "comparison": "literal" + } + ], + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": "Awesome" + } + ], + "site": { + "page": "test.com" + } + }, + "httpCalls": [] +} diff --git a/adapters/afront/afronttest/supplemental/invalid-response.json b/adapters/afront/afronttest/supplemental/invalid-response.json new file mode 100644 index 00000000000..b3dce949f5d --- /dev/null +++ b/adapters/afront/afronttest/supplemental/invalid-response.json @@ -0,0 +1,115 @@ +{ + "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": "invalid response" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Bad Server Response", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/supplemental/status-code-bad-request.json b/adapters/afront/afronttest/supplemental/status-code-bad-request.json new file mode 100644 index 00000000000..5a508e59bac --- /dev/null +++ b/adapters/afront/afronttest/supplemental/status-code-bad-request.json @@ -0,0 +1,96 @@ +{ + "mockBidRequest": { + "id": "req-id", + "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", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "accountId": "accountId", + "sourceId": "sourceId" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://snt1.afront.io/?rsd=sourceId&sk=accountId", + "body": { + "id": "req-id", + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "ogTAGID" + } + ], + "app": { + "publisher": { + "id": "3998392" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "3998392" + }, + "user": { + "buyeruid": "test-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "some-impression-id" + ] + }, + "mockResponse": { + "status": 400 + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/supplemental/status-code-no-content.json b/adapters/afront/afronttest/supplemental/status-code-no-content.json new file mode 100644 index 00000000000..6dde8c82b52 --- /dev/null +++ b/adapters/afront/afronttest/supplemental/status-code-no-content.json @@ -0,0 +1,79 @@ +{ + "mockBidRequest": { + "id": "req-id", + "tmax": 1000, + "user": { + "buyeruid": "test-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "accountId": "accountId", + "sourceId": "sourceId" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://snt1.afront.io/?rsd=sourceId&sk=accountId", + "body": { + "id": "req-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "user": { + "buyeruid": "test-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "some-impression-id" + ] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/supplemental/status-code-other-error.json b/adapters/afront/afronttest/supplemental/status-code-other-error.json new file mode 100644 index 00000000000..42048626a7f --- /dev/null +++ b/adapters/afront/afronttest/supplemental/status-code-other-error.json @@ -0,0 +1,84 @@ +{ + "mockBidRequest": { + "id": "req-id", + "tmax": 1000, + "user": { + "buyeruid": "test-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "accountId": "accountId", + "sourceId": "sourceId" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://snt1.afront.io/?rsd=sourceId&sk=accountId", + "body": { + "id": "req-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "user": { + "buyeruid": "test-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "some-impression-id" + ] + }, + "mockResponse": { + "status": 306 + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 306. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/afront/afronttest/supplemental/status-code-service-unavailable.json b/adapters/afront/afronttest/supplemental/status-code-service-unavailable.json new file mode 100644 index 00000000000..5df41e0c6a2 --- /dev/null +++ b/adapters/afront/afronttest/supplemental/status-code-service-unavailable.json @@ -0,0 +1,84 @@ +{ + "mockBidRequest": { + "id": "req-id", + "tmax": 1000, + "user": { + "buyeruid": "test-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "accountId": "accountId", + "sourceId": "sourceId" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://snt1.afront.io/?rsd=sourceId&sk=accountId", + "body": { + "id": "req-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "3998392" + } + }, + "user": { + "buyeruid": "test-user" + }, + "tmax": 1000 + }, + "impIDs": [ + "some-impression-id" + ] + }, + "mockResponse": { + "status": 503 + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 503. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/afront/params_test.go b/adapters/afront/params_test.go new file mode 100644 index 00000000000..b50ff16425a --- /dev/null +++ b/adapters/afront/params_test.go @@ -0,0 +1,50 @@ +package afront + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v3/openrtb_ext" +) + +var validParams = []string{ + `{ "sourceId": "partner", "accountId": "hash" }`, +} + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderAfront, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected Afront params: %s", validParam) + } + } +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"adCode": "string", "seatCode": 5, "originalPublisherid": "string"}`, + `{ "accountid": "" }`, +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderAfront, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 87821fd0c75..1c8f495a12e 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -35,6 +35,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/adview" "github.com/prebid/prebid-server/v3/adapters/adxcg" "github.com/prebid/prebid-server/v3/adapters/adyoulike" + "github.com/prebid/prebid-server/v3/adapters/afront" "github.com/prebid/prebid-server/v3/adapters/aidem" "github.com/prebid/prebid-server/v3/adapters/aja" "github.com/prebid/prebid-server/v3/adapters/algorix" @@ -284,6 +285,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderAdView: adview.Builder, openrtb_ext.BidderAdxcg: adxcg.Builder, openrtb_ext.BidderAdyoulike: adyoulike.Builder, + openrtb_ext.BidderAfront: afront.Builder, openrtb_ext.BidderAidem: aidem.Builder, openrtb_ext.BidderAJA: aja.Builder, openrtb_ext.BidderAlgorix: algorix.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 67cf8e7c15e..c6c16b86fe1 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -51,6 +51,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderAdView, BidderAdxcg, BidderAdyoulike, + BidderAfront, BidderAidem, BidderAJA, BidderAlgorix, @@ -407,6 +408,7 @@ const ( BidderAdView BidderName = "adview" BidderAdxcg BidderName = "adxcg" BidderAdyoulike BidderName = "adyoulike" + BidderAfront BidderName = "afront" BidderAidem BidderName = "aidem" BidderAJA BidderName = "aja" BidderAlgorix BidderName = "algorix" diff --git a/openrtb_ext/imp_afront.go b/openrtb_ext/imp_afront.go new file mode 100644 index 00000000000..45d692964e5 --- /dev/null +++ b/openrtb_ext/imp_afront.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ExtAfront struct { + AccountID string `json:"accountId"` + SourceId string `json:"sourceId"` +} diff --git a/static/bidder-info/afront.yaml b/static/bidder-info/afront.yaml new file mode 100644 index 00000000000..4a233c3ed3b --- /dev/null +++ b/static/bidder-info/afront.yaml @@ -0,0 +1,23 @@ +# Contact support@afront.io to connect with Afront exchange. +# We have the following regional endpoint sub-domains: +# US East: snt1 +# APAC: snt2 +# EU: snt3 +# Please deploy this config in each of your datacenters with the appropriate regional subdomain +endpoint: "https://snt1.afront.io/?rsd={{.SourceId}}&sk={{.AccountID}}" +maintainer: + email: "support@afront.io" +geoscope: + - global +endpointCompression: "GZIP" +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-params/afront.json b/static/bidder-params/afront.json new file mode 100644 index 00000000000..c472dd3b591 --- /dev/null +++ b/static/bidder-params/afront.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Afront Adapter Params", + "description": "A schema which validates params accepted by the Afront adapter", + "type": "object", + "properties": { + "accountId": { + "type": "string", + "description": "Client account id", + "minLength": 1 + }, + "sourceId": { + "type": "string", + "description": "Data source id", + "minLength": 1 + } + }, + "required": [ + "accountId", + "sourceId" + ] +} \ No newline at end of file