-
Notifications
You must be signed in to change notification settings - Fork 902
New Adapter: Waardex #4563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yegorWaardex
wants to merge
32
commits into
prebid:master
Choose a base branch
from
yegorWaardex:add-waardex-adapter
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
New Adapter: Waardex #4563
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
2d0320b
Add Waardex bidder integration and tests
yegorHeiz de150e5
Refactor Waardex MakeRequests to ignore unused reqInfo argument
yegorHeiz 9579219
Simplify error handling in Waardex MakeRequests function
yegorHeiz cdce8ef
Refactor Waardex MakeRequests error handling to improve clarity
yegorHeiz 13a2eec
Refactor Waardex dispatchImpressions to simplify return structure and…
yegorHeiz 10f656f
Refactor Waardex isMultiFormatImp to improve variable naming clarity
yegorHeiz eea4da4
Refactor error formatting in Waardex Unmarshal to use %v for consistency
yegorHeiz c662bfa
Refactor Waardex MakeRequests to improve variable naming consistency
yegorHeiz 3b9301d
Refactor Waardex createBidRequest to remove unused params argument an…
yegorHeiz 531e3e5
Refactor Waardex createBidRequest to simplify site and app copy logic
yegorHeiz bdafa95
Refactor Waardex MakeBids to ensure Currency is set only when available
yegorHeiz 9300235
Refactor Waardex validateImpression logic into core JSON schema and s…
yegorHeiz 1796660
Refactor Waardex dispatchImpressions to initialize map with capacity …
yegorHeiz 27de5b2
Add comprehensive unit tests for Waardex adapter functions and utilities
yegorHeiz f2f8c97
Refactor Waardex MakeBids to aggregate errors instead of returning early
yegorHeiz c932712
Refactor Waardex adapter to use consistent indentation and add new un…
yegorHeiz f207127
Add unit tests for isMultiFormatImp to cover diverse impression cases
yegorHeiz 43c3d05
Add unit tests for getImpressionExt to handle malformed and invalid b…
yegorHeiz 4a1646a
Add unit tests for MakeRequests to handle invalid and malformed impre…
yegorHeiz 482bb52
Refactor Waardex MakeRequests to remove redundant no-impression check…
yegorHeiz 1f2485b
Update unit test expected error messages for malformed impression ext…
yegorHeiz 1a97dd6
Optimize memory allocation in Waardex splitMultiFormatImp by reducing…
yegorHeiz 16eeb26
Refactor Waardex adapter by merging `getImpressionsInfo` and `dispatc…
yegorHeiz d024330
Reduce initial slice capacity in Waardex adapter to optimize memory a…
yegorHeiz c1fdb37
Append `sspID` to Waardex user-sync redirect URL for enhanced tracking
yegorHeiz 63861c2
Update Waardex user-sync configuration to require host setup and add …
yegorHeiz f3f8181
Reorder Waardex entry in adapter builders for consistent alphabetical…
yegorHeiz d298129
Add supplemental test cases for Waardex to validate error handling of…
yegorHeiz ab7c2e8
Add unit tests for Waardex to validate bidder parameter schema
yegorHeiz ae54a8a
Rename waardex adapter struct
yegorHeiz 0bcfaf6
Add exemplary test cases for Waardex adapter to validate multiple imp…
yegorHeiz a8436cc
Remove unused `newBadInputError` function from Waardex adapter
yegorHeiz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| package waardex | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "testing" | ||
|
|
||
| "github.com/prebid/prebid-server/v3/openrtb_ext" | ||
| ) | ||
|
|
||
| func TestValidParams(t *testing.T) { | ||
| validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") | ||
| if err != nil { | ||
| t.Fatalf("Failed to fetch the json schema. %v", err) | ||
| } | ||
|
|
||
| for _, p := range validParams { | ||
| if err := validator.Validate(openrtb_ext.BidderWaardex, json.RawMessage(p)); err != nil { | ||
| t.Errorf("Schema rejected valid params: %s", p) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func TestInvalidParams(t *testing.T) { | ||
| validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") | ||
| if err != nil { | ||
| t.Fatalf("Failed to fetch the json schema. %v", err) | ||
| } | ||
|
|
||
| for _, p := range invalidParams { | ||
| if err := validator.Validate(openrtb_ext.BidderWaardex, json.RawMessage(p)); err == nil { | ||
| t.Errorf("Schema allowed invalid params: %s", p) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| var validParams = []string{ | ||
| `{ | ||
| "zoneId": 1 | ||
| }`, | ||
| `{ | ||
| "zoneId": 12345 | ||
| }`, | ||
| } | ||
|
|
||
| var invalidParams = []string{ | ||
| ``, | ||
| `null`, | ||
| `true`, | ||
| `5`, | ||
| `4.2`, | ||
| `[]`, | ||
| `{}`, | ||
| `{ | ||
| "zoneId": 0 | ||
| }`, | ||
| `{ | ||
| "zoneId": -1 | ||
| }`, | ||
| `{ | ||
| "zoneId": "1" | ||
| }`, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,255 @@ | ||
| package waardex | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "net/http" | ||
| "strconv" | ||
| "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 { | ||
| EndpointTemplate *template.Template | ||
| } | ||
|
|
||
| // MakeRequests prepares request information for prebid-server core | ||
| func (adapter *adapter) MakeRequests(request *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { | ||
| var errs []error | ||
| impressionsByZone, impErrs := groupImpressionsByZone(request.Imp) | ||
| errs = append(errs, impErrs...) | ||
| if len(impressionsByZone) == 0 { | ||
| return nil, errs | ||
| } | ||
| result := make([]*adapters.RequestData, 0, len(impressionsByZone)) | ||
| for k, imps := range impressionsByZone { | ||
| bidRequest, err := adapter.buildAdapterRequest(request, &k, imps) | ||
| if err != nil { | ||
| errs = append(errs, err) | ||
| } else { | ||
| result = append(result, bidRequest) | ||
| } | ||
| } | ||
| return result, errs | ||
| } | ||
|
|
||
| // groupImpressionsByZone validates imps and groups them by Waardex-specific parameter `zoneId`. | ||
| func groupImpressionsByZone(imps []openrtb2.Imp) (map[openrtb_ext.ExtImpWaardex][]openrtb2.Imp, []error) { | ||
| res := make(map[openrtb_ext.ExtImpWaardex][]openrtb2.Imp) | ||
| errors := make([]error, 0, len(imps)) | ||
| for idx := range imps { | ||
| imp := imps[idx] | ||
| impExt, err := getImpressionExt(&imp) | ||
| if err != nil { | ||
| errors = append(errors, err) | ||
| continue | ||
| } | ||
| imp.Ext = nil | ||
| // Additional validation is handled by the core JSON schema (static/bidder-params/waardex.json). | ||
| if isMultiFormatImp(&imp) { | ||
| splImps := splitMultiFormatImp(&imp) | ||
| if len(splImps) == 0 { | ||
| continue | ||
| } | ||
| if _, exists := res[*impExt]; !exists { | ||
| res[*impExt] = make([]openrtb2.Imp, 0, 2) | ||
| } | ||
| res[*impExt] = append(res[*impExt], splImps...) | ||
| } else { | ||
| if _, exists := res[*impExt]; !exists { | ||
| res[*impExt] = make([]openrtb2.Imp, 0, 2) | ||
| } | ||
| res[*impExt] = append(res[*impExt], imp) | ||
| } | ||
| } | ||
| return res, errors | ||
| } | ||
|
|
||
| func isMultiFormatImp(imp *openrtb2.Imp) bool { | ||
|
yegorWaardex marked this conversation as resolved.
|
||
| formatCount := 0 | ||
| if imp.Video != nil { | ||
| formatCount++ | ||
| } | ||
| if imp.Audio != nil { | ||
| formatCount++ | ||
| } | ||
| if imp.Banner != nil { | ||
| formatCount++ | ||
| } | ||
| if imp.Native != nil { | ||
| formatCount++ | ||
| } | ||
| return formatCount > 1 | ||
| } | ||
|
|
||
| func splitMultiFormatImp(imp *openrtb2.Imp) []openrtb2.Imp { | ||
| splitImps := make([]openrtb2.Imp, 0, 2) | ||
| if imp.Banner != nil { | ||
| impCopy := *imp | ||
| impCopy.Video = nil | ||
| impCopy.Native = nil | ||
| impCopy.Audio = nil | ||
| splitImps = append(splitImps, impCopy) | ||
| } | ||
| if imp.Video != nil { | ||
| impCopy := *imp | ||
| impCopy.Banner = nil | ||
| impCopy.Native = nil | ||
| impCopy.Audio = nil | ||
| splitImps = append(splitImps, impCopy) | ||
| } | ||
| return splitImps | ||
| } | ||
|
|
||
| func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpWaardex, error) { | ||
| var bidderExt adapters.ExtImpBidder | ||
| if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil { | ||
| return nil, &errortypes.BadInput{ | ||
|
yegorWaardex marked this conversation as resolved.
|
||
| Message: fmt.Sprintf("imp.ext is invalid: %s", err.Error()), | ||
| } | ||
| } | ||
| var waardexExt openrtb_ext.ExtImpWaardex | ||
| if err := jsonutil.Unmarshal(bidderExt.Bidder, &waardexExt); err != nil { | ||
| return nil, &errortypes.BadInput{ | ||
| Message: fmt.Sprintf("imp.ext.bidder is invalid: %s", err.Error()), | ||
| } | ||
| } | ||
| return &waardexExt, nil | ||
| } | ||
|
|
||
| func (adapter *adapter) buildAdapterRequest(prebidBidRequest *openrtb2.BidRequest, params *openrtb_ext.ExtImpWaardex, imps []openrtb2.Imp) (*adapters.RequestData, error) { | ||
| newBidRequest := createBidRequest(prebidBidRequest, imps) | ||
| reqJSON, err := json.Marshal(newBidRequest) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| headers := http.Header{} | ||
| headers.Add("Content-Type", "application/json;charset=utf-8") | ||
| headers.Add("Accept", "application/json") | ||
| headers.Add("x-openrtb-version", "2.5") | ||
|
|
||
| url, err := adapter.buildEndpointURL(params) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return &adapters.RequestData{ | ||
| Method: "POST", | ||
| Uri: url, | ||
| Body: reqJSON, | ||
| Headers: headers, | ||
| ImpIDs: openrtb_ext.GetImpIDs(imps)}, nil | ||
| } | ||
|
|
||
| func createBidRequest(prebidBidRequest *openrtb2.BidRequest, imps []openrtb2.Imp) *openrtb2.BidRequest { | ||
| bidRequest := *prebidBidRequest | ||
| bidRequest.Imp = imps | ||
| if bidRequest.Site != nil { | ||
| // Need to copy Site as Request is a shallow copy | ||
| site := *bidRequest.Site | ||
| site.Publisher = nil | ||
| bidRequest.Site = &site | ||
| } | ||
| if bidRequest.App != nil { | ||
| // Need to copy App as Request is a shallow copy | ||
| app := *bidRequest.App | ||
| app.Publisher = nil | ||
| bidRequest.App = &app | ||
| } | ||
| return &bidRequest | ||
| } | ||
|
|
||
| // Builds endpoint url based on adapter-specific pub settings from imp.ext | ||
| func (adapter *adapter) buildEndpointURL(params *openrtb_ext.ExtImpWaardex) (string, error) { | ||
| endpointParams := macros.EndpointTemplateParams{ZoneID: strconv.Itoa(params.ZoneId)} | ||
| return macros.ResolveMacros(adapter.EndpointTemplate, endpointParams) | ||
| } | ||
|
|
||
| // MakeBids translates Waardex bid response to prebid-server specific format | ||
| func (adapter *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { | ||
| if response.StatusCode == http.StatusNoContent { | ||
| return nil, nil | ||
| } | ||
| if response.StatusCode != http.StatusOK { | ||
| return nil, []error{ | ||
| newBadServerResponseError(fmt.Sprintf("Unexpected http status code: %d", response.StatusCode)), | ||
| } | ||
| } | ||
| var bidResp openrtb2.BidResponse | ||
| if err := jsonutil.Unmarshal(response.Body, &bidResp); err != nil { | ||
| return nil, []error{ | ||
| newBadServerResponseError(fmt.Sprintf("Bad server response: %v", err)), | ||
| } | ||
| } | ||
|
|
||
| if len(bidResp.SeatBid) != 1 { | ||
| return nil, []error{ | ||
| newBadServerResponseError(fmt.Sprintf("Invalid SeatBids count: %d", len(bidResp.SeatBid))), | ||
| } | ||
| } | ||
|
|
||
| seatBid := bidResp.SeatBid[0] | ||
| bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) | ||
| if bidResp.Cur != "" { | ||
| bidResponse.Currency = bidResp.Cur | ||
| } | ||
| var errs []error | ||
| for i := 0; i < len(seatBid.Bid); i++ { | ||
| bid := seatBid.Bid[i] | ||
| bidType, err := getMediaTypeForBid(&bid) | ||
| if err != nil { | ||
| errs = append(errs, err) | ||
| continue | ||
| } | ||
| bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ | ||
| Bid: &bid, | ||
| BidType: bidType, | ||
| }) | ||
| } | ||
| return bidResponse, errs | ||
| } | ||
|
|
||
| // getMediaTypeForImp figures out which media type this bid is for | ||
| func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) { | ||
| switch bid.MType { | ||
| case openrtb2.MarkupBanner: | ||
| return openrtb_ext.BidTypeBanner, nil | ||
| case openrtb2.MarkupAudio: | ||
| return openrtb_ext.BidTypeAudio, nil | ||
| case openrtb2.MarkupNative: | ||
| return openrtb_ext.BidTypeNative, nil | ||
| case openrtb2.MarkupVideo: | ||
| return openrtb_ext.BidTypeVideo, nil | ||
| default: | ||
| return "", &errortypes.BadServerResponse{ | ||
| Message: fmt.Sprintf("Unsupported MType %d", bid.MType), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func newBadServerResponseError(message string) error { | ||
| return &errortypes.BadServerResponse{ | ||
| Message: message, | ||
| } | ||
| } | ||
|
|
||
| // Builder builds a new instance of the waardex adapter for the given bidder with the given config. | ||
| func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { | ||
| urlTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) | ||
| } | ||
|
|
||
| bidder := &adapter{ | ||
| EndpointTemplate: urlTemplate, | ||
| } | ||
| return bidder, nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package waardex | ||
|
|
||
|
yegorWaardex marked this conversation as resolved.
|
||
| 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) { | ||
|
yegorWaardex marked this conversation as resolved.
|
||
| bidder, buildErr := Builder(openrtb_ext.BidderWaardex, config.Adapter{ | ||
| Endpoint: "http://justbidit2.xyz:8800/hb?zone={{.ZoneID}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) | ||
|
|
||
| if buildErr != nil { | ||
| t.Fatalf("Builder returned unexpected error %v", buildErr) | ||
| } | ||
|
|
||
| adapterstest.RunJSONBidderTest(t, "waardextest", bidder) | ||
| } | ||
|
|
||
| func TestEndpointTemplateMalformed(t *testing.T) { | ||
| _, buildErr := Builder(openrtb_ext.BidderWaardex, config.Adapter{ | ||
| Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) | ||
|
|
||
| assert.Error(t, buildErr) | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.