github.com/prebid/prebid-server/v2@v2.18.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/v20/openrtb2" 9 "github.com/prebid/prebid-server/v2/adapters" 10 "github.com/prebid/prebid-server/v2/config" 11 "github.com/prebid/prebid-server/v2/errortypes" 12 "github.com/prebid/prebid-server/v2/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 ImpIDs: openrtb_ext.GetImpIDs(requestCopy.Imp), 135 } 136 requests = append(requests, requestData) 137 } 138 139 return requests, errs 140 } 141 142 /* MakeBids */ 143 144 func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) { 145 if bid == nil { 146 return "", fmt.Errorf("the bid request object is nil") 147 } 148 149 var bidExt pangleBidExt 150 if err := json.Unmarshal(bid.Ext, &bidExt); err != nil { 151 return "", fmt.Errorf("invalid bid ext") 152 } else if bidExt.Pangle == nil || bidExt.Pangle.AdType == nil { 153 return "", fmt.Errorf("missing pangleExt/adtype in bid ext") 154 } 155 156 switch *bidExt.Pangle.AdType { 157 case 1: 158 return openrtb_ext.BidTypeBanner, nil 159 case 2: 160 return openrtb_ext.BidTypeBanner, nil 161 case 5: 162 return openrtb_ext.BidTypeNative, nil 163 case 7: 164 return openrtb_ext.BidTypeVideo, nil 165 case 8: 166 return openrtb_ext.BidTypeVideo, nil 167 } 168 169 return "", fmt.Errorf("unrecognized adtype in response") 170 } 171 172 func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { 173 if responseData.StatusCode == http.StatusNoContent { 174 return nil, nil 175 } 176 177 if responseData.StatusCode == http.StatusBadRequest { 178 err := &errortypes.BadInput{ 179 Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.", 180 } 181 return nil, []error{err} 182 } 183 184 if responseData.StatusCode != http.StatusOK { 185 err := &errortypes.BadServerResponse{ 186 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode), 187 } 188 return nil, []error{err} 189 } 190 191 var response openrtb2.BidResponse 192 if err := json.Unmarshal(responseData.Body, &response); err != nil { 193 return nil, []error{err} 194 } 195 196 var errs []error 197 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 198 bidResponse.Currency = response.Cur 199 for _, seatBid := range response.SeatBid { 200 for _, temp := range seatBid.Bid { 201 bid := temp // avoid taking address of a for loop variable 202 mediaType, err := getMediaTypeForBid(&bid) 203 if err != nil { 204 errs = append(errs, err) 205 continue 206 } 207 b := &adapters.TypedBid{ 208 Bid: &bid, 209 BidType: mediaType, 210 } 211 bidResponse.Bids = append(bidResponse.Bids, b) 212 } 213 } 214 215 return bidResponse, errs 216 }