github.com/prebid/prebid-server/v2@v2.18.0/exchange/events.go (about) 1 package exchange 2 3 import ( 4 "time" 5 6 "github.com/prebid/prebid-server/v2/exchange/entities" 7 jsonpatch "gopkg.in/evanphx/json-patch.v4" 8 9 "github.com/prebid/prebid-server/v2/analytics" 10 "github.com/prebid/prebid-server/v2/config" 11 "github.com/prebid/prebid-server/v2/endpoints/events" 12 "github.com/prebid/prebid-server/v2/openrtb_ext" 13 "github.com/prebid/prebid-server/v2/util/jsonutil" 14 ) 15 16 // eventTracking has configuration fields needed for adding event tracking to an auction response 17 type eventTracking struct { 18 accountID string 19 enabledForAccount bool 20 enabledForRequest bool 21 auctionTimestampMs int64 22 integrationType string 23 bidderInfos config.BidderInfos 24 externalURL string 25 } 26 27 // getEventTracking creates an eventTracking object from the different configuration sources 28 func getEventTracking(requestExtPrebid *openrtb_ext.ExtRequestPrebid, ts time.Time, account *config.Account, bidderInfos config.BidderInfos, externalURL string) *eventTracking { 29 return &eventTracking{ 30 accountID: account.ID, 31 enabledForAccount: account.Events.Enabled, 32 enabledForRequest: requestExtPrebid != nil && requestExtPrebid.Events != nil, 33 auctionTimestampMs: ts.UnixNano() / 1e+6, 34 integrationType: getIntegrationType(requestExtPrebid), 35 bidderInfos: bidderInfos, 36 externalURL: externalURL, 37 } 38 } 39 40 func getIntegrationType(requestExtPrebid *openrtb_ext.ExtRequestPrebid) string { 41 if requestExtPrebid != nil { 42 return requestExtPrebid.Integration 43 } 44 return "" 45 } 46 47 // modifyBidsForEvents adds bidEvents and modifies VAST AdM if necessary. 48 func (ev *eventTracking) modifyBidsForEvents(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid) map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid { 49 for bidderName, seatBid := range seatBids { 50 modifyingVastXMLAllowed := ev.isModifyingVASTXMLAllowed(bidderName.String()) 51 for _, pbsBid := range seatBid.Bids { 52 if modifyingVastXMLAllowed { 53 ev.modifyBidVAST(pbsBid, bidderName) 54 } 55 pbsBid.BidEvents = ev.makeBidExtEvents(pbsBid, bidderName) 56 } 57 } 58 return seatBids 59 } 60 61 // isModifyingVASTXMLAllowed returns true if this bidder config allows modifying VAST XML for event tracking 62 func (ev *eventTracking) isModifyingVASTXMLAllowed(bidderName string) bool { 63 return ev.bidderInfos[bidderName].ModifyingVastXmlAllowed && ev.isEventAllowed() 64 } 65 66 // modifyBidVAST injects event Impression url if needed, otherwise returns original VAST string 67 func (ev *eventTracking) modifyBidVAST(pbsBid *entities.PbsOrtbBid, bidderName openrtb_ext.BidderName) { 68 bid := pbsBid.Bid 69 if pbsBid.BidType != openrtb_ext.BidTypeVideo || len(bid.AdM) == 0 && len(bid.NURL) == 0 { 70 return 71 } 72 vastXML := makeVAST(bid) 73 bidID := bid.ID 74 if len(pbsBid.GeneratedBidID) > 0 { 75 bidID = pbsBid.GeneratedBidID 76 } 77 if newVastXML, ok := events.ModifyVastXmlString(ev.externalURL, vastXML, bidID, bidderName.String(), ev.accountID, ev.auctionTimestampMs, ev.integrationType); ok { 78 bid.AdM = newVastXML 79 } 80 } 81 82 // modifyBidJSON injects "wurl" (win) event url if needed, otherwise returns original json 83 func (ev *eventTracking) modifyBidJSON(pbsBid *entities.PbsOrtbBid, bidderName openrtb_ext.BidderName, jsonBytes []byte) ([]byte, error) { 84 if !ev.isEventAllowed() || pbsBid.BidType == openrtb_ext.BidTypeVideo { 85 return jsonBytes, nil 86 } 87 var winEventURL string 88 if pbsBid.BidEvents != nil { // All bids should have already been updated with win/imp event URLs 89 winEventURL = pbsBid.BidEvents.Win 90 } else { 91 winEventURL = ev.makeEventURL(analytics.Win, pbsBid, bidderName) 92 } 93 // wurl attribute is not in the schema, so we have to patch 94 patch, err := jsonutil.Marshal(map[string]string{"wurl": winEventURL}) 95 if err != nil { 96 return jsonBytes, err 97 } 98 modifiedJSON, err := jsonpatch.MergePatch(jsonBytes, patch) 99 if err != nil { 100 return jsonBytes, err 101 } 102 return modifiedJSON, nil 103 } 104 105 // makeBidExtEvents make the data for bid.ext.prebid.events if needed, otherwise returns nil 106 func (ev *eventTracking) makeBidExtEvents(pbsBid *entities.PbsOrtbBid, bidderName openrtb_ext.BidderName) *openrtb_ext.ExtBidPrebidEvents { 107 if !ev.isEventAllowed() || pbsBid.BidType == openrtb_ext.BidTypeVideo { 108 return nil 109 } 110 return &openrtb_ext.ExtBidPrebidEvents{ 111 Win: ev.makeEventURL(analytics.Win, pbsBid, bidderName), 112 Imp: ev.makeEventURL(analytics.Imp, pbsBid, bidderName), 113 } 114 } 115 116 // makeEventURL returns an analytics event url for the requested type (win or imp) 117 func (ev *eventTracking) makeEventURL(evType analytics.EventType, pbsBid *entities.PbsOrtbBid, bidderName openrtb_ext.BidderName) string { 118 bidId := pbsBid.Bid.ID 119 if len(pbsBid.GeneratedBidID) > 0 { 120 bidId = pbsBid.GeneratedBidID 121 } 122 return events.EventRequestToUrl(ev.externalURL, 123 &analytics.EventRequest{ 124 Type: evType, 125 BidID: bidId, 126 Bidder: string(bidderName), 127 AccountID: ev.accountID, 128 Timestamp: ev.auctionTimestampMs, 129 Integration: ev.integrationType, 130 }) 131 } 132 133 // isEventAllowed checks if events are enabled by default or on account/request level 134 func (ev *eventTracking) isEventAllowed() bool { 135 return ev.enabledForAccount || ev.enabledForRequest 136 }