github.com/prebid/prebid-server@v0.275.0/adapters/pangle/pangle.go (about) 1 package pangle 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 8 "github.com/prebid/openrtb/v19/openrtb2" 9 "github.com/prebid/prebid-server/adapters" 10 "github.com/prebid/prebid-server/config" 11 "github.com/prebid/prebid-server/errortypes" 12 "github.com/prebid/prebid-server/openrtb_ext" 13 ) 14 15 type adapter struct { 16 Endpoint string 17 } 18 19 type NetworkIDs struct { 20 AppID string `json:"appid,omitempty"` 21 PlacementID string `json:"placementid,omitempty"` 22 } 23 24 type wrappedExtImpBidder struct { 25 *adapters.ExtImpBidder 26 AdType int `json:"adtype,omitempty"` 27 IsPrebid bool `json:"is_prebid,omitempty"` 28 NetworkIDs *NetworkIDs `json:"networkids,omitempty"` 29 } 30 31 type pangleBidExt struct { 32 Pangle *bidExt `json:"pangle,omitempty"` 33 } 34 35 type bidExt struct { 36 AdType *int `json:"adtype,omitempty"` 37 } 38 39 /* Builder */ 40 41 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 42 bidder := &adapter{ 43 Endpoint: config.Endpoint, 44 } 45 46 return bidder, nil 47 } 48 49 /* MakeRequests */ 50 51 func getAdType(imp openrtb2.Imp, parsedImpExt *wrappedExtImpBidder) int { 52 // video 53 if imp.Video != nil { 54 if parsedImpExt != nil && parsedImpExt.Prebid != nil && parsedImpExt.Prebid.IsRewardedInventory != nil && *parsedImpExt.Prebid.IsRewardedInventory == 1 { 55 return 7 56 } 57 if imp.Instl == 1 { 58 return 8 59 } 60 } 61 // banner 62 if imp.Banner != nil { 63 if imp.Instl == 1 { 64 return 2 65 } else { 66 return 1 67 } 68 } 69 // native 70 if imp.Native != nil && len(imp.Native.Request) > 0 { 71 return 5 72 } 73 74 return -1 75 } 76 77 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 78 var requests []*adapters.RequestData 79 var errs []error 80 81 requestCopy := *request 82 for _, imp := range request.Imp { 83 var impExt wrappedExtImpBidder 84 if err := json.Unmarshal(imp.Ext, &impExt); err != nil { 85 errs = append(errs, fmt.Errorf("failed unmarshalling imp ext (err)%s", err.Error())) 86 continue 87 } 88 // get token & networkIDs 89 var bidderImpExt openrtb_ext.ImpExtPangle 90 if err := json.Unmarshal(impExt.Bidder, &bidderImpExt); err != nil { 91 errs = append(errs, fmt.Errorf("failed unmarshalling bidder imp ext (err)%s", err.Error())) 92 continue 93 } 94 // detect and fill adtype 95 adType := getAdType(imp, &impExt) 96 if adType == -1 { 97 errs = append(errs, &errortypes.BadInput{Message: "not a supported adtype"}) 98 continue 99 } 100 // remarshal imp.ext 101 impExt.AdType = adType 102 impExt.IsPrebid = true 103 if len(bidderImpExt.AppID) > 0 && len(bidderImpExt.PlacementID) > 0 { 104 impExt.NetworkIDs = &NetworkIDs{ 105 AppID: bidderImpExt.AppID, 106 PlacementID: bidderImpExt.PlacementID, 107 } 108 } else if len(bidderImpExt.AppID) > 0 || len(bidderImpExt.PlacementID) > 0 { 109 errs = append(errs, &errortypes.BadInput{Message: "only one of appid or placementid is provided"}) 110 continue 111 } 112 if newImpExt, err := json.Marshal(impExt); err == nil { 113 imp.Ext = newImpExt 114 } else { 115 errs = append(errs, fmt.Errorf("failed re-marshalling imp ext")) 116 continue 117 } 118 119 requestCopy.Imp = []openrtb2.Imp{imp} 120 requestJSON, err := json.Marshal(requestCopy) 121 if err != nil { 122 errs = append(errs, err) 123 continue 124 } 125 126 requestData := &adapters.RequestData{ 127 Method: "POST", 128 Uri: a.Endpoint, 129 Body: requestJSON, 130 Headers: http.Header{ 131 "TOKEN": []string{bidderImpExt.Token}, 132 "Content-Type": []string{"application/json"}, 133 }, 134 } 135 requests = append(requests, requestData) 136 } 137 138 return requests, errs 139 } 140 141 /* MakeBids */ 142 143 func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) { 144 if bid == nil { 145 return "", fmt.Errorf("the bid request object is nil") 146 } 147 148 var bidExt pangleBidExt 149 if err := json.Unmarshal(bid.Ext, &bidExt); err != nil { 150 return "", fmt.Errorf("invalid bid ext") 151 } else if bidExt.Pangle == nil || bidExt.Pangle.AdType == nil { 152 return "", fmt.Errorf("missing pangleExt/adtype in bid ext") 153 } 154 155 switch *bidExt.Pangle.AdType { 156 case 1: 157 return openrtb_ext.BidTypeBanner, nil 158 case 2: 159 return openrtb_ext.BidTypeBanner, nil 160 case 5: 161 return openrtb_ext.BidTypeNative, nil 162 case 7: 163 return openrtb_ext.BidTypeVideo, nil 164 case 8: 165 return openrtb_ext.BidTypeVideo, nil 166 } 167 168 return "", fmt.Errorf("unrecognized adtype in response") 169 } 170 171 func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { 172 if responseData.StatusCode == http.StatusNoContent { 173 return nil, nil 174 } 175 176 if responseData.StatusCode == http.StatusBadRequest { 177 err := &errortypes.BadInput{ 178 Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.", 179 } 180 return nil, []error{err} 181 } 182 183 if responseData.StatusCode != http.StatusOK { 184 err := &errortypes.BadServerResponse{ 185 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode), 186 } 187 return nil, []error{err} 188 } 189 190 var response openrtb2.BidResponse 191 if err := json.Unmarshal(responseData.Body, &response); err != nil { 192 return nil, []error{err} 193 } 194 195 var errs []error 196 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 197 bidResponse.Currency = response.Cur 198 for _, seatBid := range response.SeatBid { 199 for _, temp := range seatBid.Bid { 200 bid := temp // avoid taking address of a for loop variable 201 mediaType, err := getMediaTypeForBid(&bid) 202 if err != nil { 203 errs = append(errs, err) 204 continue 205 } 206 b := &adapters.TypedBid{ 207 Bid: &bid, 208 BidType: mediaType, 209 } 210 bidResponse.Bids = append(bidResponse.Bids, b) 211 } 212 } 213 214 return bidResponse, errs 215 }