-
Notifications
You must be signed in to change notification settings - Fork 867
New Adapter: targetVideo #4593
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
base: master
Are you sure you want to change the base?
New Adapter: targetVideo #4593
Changes from 3 commits
d48e042
197d704
85c95b5
192ff51
b5eebc5
e0eac3d
8331306
377a133
4fe67ea
5e5d533
fc2551a
a2ef146
9504036
66c82f6
92a1b1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package targetVideo | ||
|
|
||
| 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-schemas. %v", err) | ||
| } | ||
|
|
||
| for _, validParam := range validParams { | ||
| if err := validator.Validate(openrtb_ext.BidderTargetVideo, json.RawMessage(validParam)); err != nil { | ||
| t.Errorf("Schema rejected targetVideo params: %s", validParam) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| 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.BidderTargetVideo, json.RawMessage(invalidParam)); err == nil { | ||
| t.Errorf("Schema allowed unexpected params: %s", invalidParam) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| var validParams = []string{ | ||
| `{"placementId":846}`, | ||
| `{"placementId":"846"}`, | ||
| } | ||
|
|
||
| var invalidParams = []string{ | ||
| `null`, | ||
| `nil`, | ||
| `undefined`, | ||
| `{"placementId": "%9"}`, | ||
| `{"publisherId": "as9""}`, | ||
| `{"placementId": true}`, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add |
||
| } | ||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,194 @@ | ||||
| package targetVideo | ||||
|
|
||||
| import ( | ||||
| "encoding/json" | ||||
| "fmt" | ||||
| "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/errortypes" | ||||
| "github.com/prebid/prebid-server/v3/openrtb_ext" | ||||
| "github.com/prebid/prebid-server/v3/util/jsonutil" | ||||
| ) | ||||
|
|
||||
| type adapter struct { | ||||
| endpoint string | ||||
| } | ||||
|
|
||||
| type impExt struct { | ||||
| TargetVideo openrtb_ext.ExtImpTargetVideo `json:"targetVideo"` | ||||
| } | ||||
|
|
||||
| func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { | ||||
| totalImps := len(request.Imp) | ||||
| errors := make([]error, 0) | ||||
linux019 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||
| adapterRequests := make([]*adapters.RequestData, 0, totalImps) | ||||
|
|
||||
| // Split multi-imp request into multiple ad server requests. SRA is currently not recommended. | ||||
| for i := 0; i < totalImps; i++ { | ||||
| if adapterReq, err := a.makeRequest(*request, request.Imp[i]); err == nil { | ||||
| adapterRequests = append(adapterRequests, adapterReq) | ||||
| } else { | ||||
| errors = append(errors, err) | ||||
| } | ||||
|
||||
| } | ||||
|
|
||||
| return adapterRequests, errors | ||||
| } | ||||
|
|
||||
| func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*adapters.RequestData, error) { | ||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're missing error path test coverage. After you've made the suggested simplifications to this function, please add additional tests to the supplemental folder to cover each of the unmarshal error paths. You can do this by simply sending a mock bid request with the data under consideration malformed or of a mismatched type. |
||||
|
|
||||
| // For now, this adapter sends one imp per request, but we still | ||||
| // iterate over all imps in the request to perform the required | ||||
| // imp.ext transformation. | ||||
| request.Imp = []openrtb2.Imp{imp} | ||||
|
|
||||
| _, errImp := validateImpAndSetExt(&imp) | ||||
| if errImp != nil { | ||||
| return nil, errImp | ||||
| } | ||||
|
|
||||
| for i := range request.Imp { | ||||
| if len(request.Imp[i].Ext) == 0 { | ||||
| continue | ||||
| } | ||||
|
||||
| var root map[string]json.RawMessage | ||||
| if err := json.Unmarshal(request.Imp[i].Ext, &root); err != nil { | ||||
| // If ext cannot be parsed, skip transformation for this imp | ||||
| continue | ||||
| } | ||||
|
||||
|
|
||||
| // Try to extract placementId from ext.bidder.targetVideo (or targetvideo) | ||||
| placementId := "" | ||||
| if bRaw, ok := root["bidder"]; ok && len(bRaw) > 0 { | ||||
| var bidder map[string]json.RawMessage | ||||
| if err := json.Unmarshal(bRaw, &bidder); err == nil { | ||||
| if placementIdRaw, ok := bidder["placementId"]; ok && len(placementIdRaw) > 0 { | ||||
|
|
||||
| var asStr string | ||||
| var asInt int64 | ||||
| if err := json.Unmarshal(placementIdRaw, &asStr); err == nil && asStr != "" { | ||||
| placementId = asStr | ||||
| } else if err := json.Unmarshal(placementIdRaw, &asInt); err == nil { | ||||
| placementId = fmt.Sprintf("%d", asInt) | ||||
| } | ||||
|
|
||||
| } | ||||
| } | ||||
| // Remove bidder node as required | ||||
| delete(root, "bidder") | ||||
| } | ||||
|
|
||||
| // If we obtained a placementId, set ext.prebid.storedrequest.id = placementId | ||||
| if placementId != "" { | ||||
| // Build prebid.storedrequest structure, preserving existing prebid if any | ||||
|
||||
| var prebid map[string]json.RawMessage | ||||
| if pr, ok := root["prebid"]; ok && len(pr) > 0 { | ||||
| _ = json.Unmarshal(pr, &prebid) | ||||
| } | ||||
| if prebid == nil { | ||||
| prebid = make(map[string]json.RawMessage) | ||||
| } | ||||
| stored := map[string]string{"id": placementId} | ||||
| storedRaw, _ := json.Marshal(stored) | ||||
| prebid["storedrequest"] = storedRaw | ||||
| prebidRaw, _ := json.Marshal(prebid) | ||||
|
||||
| root["prebid"] = prebidRaw | ||||
| } | ||||
|
|
||||
| // Marshal back the transformed ext | ||||
| if newExt, err := json.Marshal(root); err == nil { | ||||
| request.Imp[i].Ext = newExt | ||||
| } | ||||
| } | ||||
|
|
||||
| reqJSON, err := json.Marshal(request) | ||||
linux019 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||
| if err != nil { | ||||
| return nil, err | ||||
| } | ||||
|
|
||||
| headers := http.Header{} | ||||
| headers.Add("Content-Type", "application/json;charset=utf-8") | ||||
| headers.Add("Accept", "application/json") | ||||
|
|
||||
| //fmt.Println("TARGET VIDEO reqJson: ", string(reqJSON)) | ||||
|
||||
| //fmt.Println("TARGET VIDEO reqJson: ", string(reqJSON)) |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use the following adapter helper functions for status code checks:
adapters/response.go#IsResponseStatusCodeNoContent
adapters/response.go#CheckResponseStatusCodeForErrors
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a supplemental test to cover this case.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you choose to keep this conditional, please add a supplemental test case to cover.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can delete these two checks because the for range on line 151 should gracefully handle the empty scenarios.
linux019 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: we don't need mediaType variable here, we can directly assign video. Also remove empty lines.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've only declared video support in your YAML file which means your adapter will only ever be called if the request impressions contain a video object. In that case, this code is unnecessary since you know the request impression will always contain this object. You can just set the mediaType to video.
If you intend to support additional media types in the future, inspecting the media type on the request like you've done here would make sense to add at that time.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this check. In your yaml file you only declared support for the video media type, in which case your adapter will only ever be called if video is not nil.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this check. Given that you declared a required bidder param that must not be empty, your adapter will only ever be called if this is not empty.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: Remove blank line
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package targetVideo | ||
|
|
||
| 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" | ||
| ) | ||
|
|
||
| func TestJsonSamples(t *testing.T) { | ||
| bidder, buildErr := Builder(openrtb_ext.BidderTargetVideo, config.Adapter{ | ||
| Endpoint: "http://localhost/pbs"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) | ||
|
|
||
| if buildErr != nil { | ||
| t.Fatalf("Builder returned unexpected error %v", buildErr) | ||
| } | ||
|
|
||
| adapterstest.RunJSONBidderTest(t, "targetvideotest", bidder) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| { | ||
| "mockBidRequest": { | ||
| "id": "test-request-id-video", | ||
| "app": { | ||
| "id": "appID", | ||
| "publisher": { | ||
| "id": "uniq_pub_id" | ||
| } | ||
| }, | ||
| "cur": ["EUR"], | ||
| "imp": [ | ||
| { | ||
| "id": "test-imp-id-video", | ||
| "video": { | ||
| "mimes": ["video/mp4"], | ||
| "w": 640, | ||
| "h": 360 | ||
| }, | ||
| "ext": { | ||
| "bidder": { | ||
| "placementId": "77777" | ||
| } | ||
| } | ||
| } | ||
| ], | ||
| "device": { | ||
| "ua": "test-user-agent" | ||
| }, | ||
| "site": { | ||
| "domain": "www.publisher.com", | ||
| "page": "http://www.publisher.com/some/path", | ||
| "ext": { | ||
| "amp": 0 | ||
| } | ||
| } | ||
|
||
| }, | ||
| "httpCalls": [ | ||
| { | ||
| "expectedRequest": { | ||
| "headers": { | ||
| "Accept": ["application/json"], | ||
| "Content-Type": ["application/json;charset=utf-8"] | ||
| }, | ||
| "uri": "http://localhost/pbs", | ||
| "body": { | ||
| "id": "test-request-id-video", | ||
| "app": { | ||
| "id": "appID", | ||
| "publisher": { | ||
| "id": "uniq_pub_id" | ||
| } | ||
| }, | ||
| "imp": [ | ||
| { | ||
| "id": "test-imp-id-video", | ||
| "video": { | ||
| "mimes": ["video/mp4"], | ||
| "w": 640, | ||
| "h": 360 | ||
| }, | ||
| "ext": { | ||
| "prebid": { | ||
| "storedrequest": { | ||
| "id": "77777" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ], | ||
| "device": { | ||
| "ua": "test-user-agent" | ||
| }, | ||
| "site": { | ||
| "domain": "www.publisher.com", | ||
| "page": "http://www.publisher.com/some/path", | ||
| "ext": { | ||
| "amp": 0 | ||
| } | ||
| }, | ||
| "cur": ["EUR"] | ||
| }, | ||
| "impIDs":["test-imp-id-video"] | ||
| }, | ||
| "mockResponse": { | ||
| "status": 200, | ||
| "body": { | ||
| "id": "test-request-id-video", | ||
| "seatbid": [ | ||
| { | ||
| "seat": "targetVideo", | ||
| "bid": [ | ||
| { | ||
| "id": "randomID", | ||
| "impid": "test-imp-id-video", | ||
| "price": 5.5, | ||
| "adid": "12345678", | ||
| "adm": "test-imp-id-video", | ||
| "cid": "789", | ||
| "crid": "12345678", | ||
| "h": 360, | ||
| "w": 640 | ||
| } | ||
| ] | ||
| } | ||
| ], | ||
| "cur": "EUR" | ||
| } | ||
| } | ||
| } | ||
| ], | ||
| "expectedBidResponses": [ | ||
| { | ||
| "currency": "EUR", | ||
| "bids" : [{ | ||
| "bid": { | ||
| "id": "randomID", | ||
| "adid": "12345678", | ||
| "impid": "test-imp-id-video", | ||
| "price": 5.5, | ||
| "adm": "test-imp-id-video", | ||
| "crid": "12345678", | ||
| "cid": "789", | ||
| "h": 360, | ||
| "w": 640 | ||
| }, | ||
| "type": "video" | ||
| }] | ||
| } | ||
| ] | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
require.NoError(t, err, "Failed to fetch the json-schemas")Please use this approach and update other tests as well