github.com/prebid/prebid-server/v2@v2.18.0/adapters/gamoshi/gamoshi.go (about) 1 package gamoshi 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 9 "github.com/prebid/openrtb/v20/openrtb2" 10 "github.com/prebid/prebid-server/v2/adapters" 11 "github.com/prebid/prebid-server/v2/config" 12 "github.com/prebid/prebid-server/v2/errortypes" 13 "github.com/prebid/prebid-server/v2/openrtb_ext" 14 ) 15 16 type GamoshiAdapter struct { 17 URI string 18 } 19 20 func (a *GamoshiAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 21 22 errs := make([]error, 0, len(request.Imp)) 23 if len(request.Imp) == 0 { 24 err := &errortypes.BadInput{ 25 Message: "No impressions in the bid request", 26 } 27 errs = append(errs, err) 28 return nil, errs 29 } 30 31 // As of now, Gamoshi supports only banner and video impressions 32 33 validImpExists := false 34 for i := 0; i < len(request.Imp); i++ { 35 if request.Imp[i].Banner != nil { 36 bannerCopy := *request.Imp[i].Banner 37 if bannerCopy.W == nil && bannerCopy.H == nil && len(bannerCopy.Format) > 0 { 38 firstFormat := bannerCopy.Format[0] 39 bannerCopy.W = &(firstFormat.W) 40 bannerCopy.H = &(firstFormat.H) 41 } 42 request.Imp[i].Banner = &bannerCopy 43 validImpExists = true 44 } else if request.Imp[i].Video != nil { 45 validImpExists = true 46 } else { 47 err := &errortypes.BadInput{ 48 Message: fmt.Sprintf("Gamoshi only supports banner and video media types. Ignoring imp id=%s", request.Imp[i].ID), 49 } 50 errs = append(errs, err) 51 request.Imp = append(request.Imp[:i], request.Imp[i+1:]...) 52 i-- 53 } 54 } 55 56 if !validImpExists { 57 err := &errortypes.BadInput{ 58 Message: fmt.Sprintf("No valid impression in the bid request"), 59 } 60 errs = append(errs, err) 61 return nil, errs 62 } 63 64 reqJSON, err := json.Marshal(request) 65 if err != nil { 66 errs = append(errs, err) 67 return nil, errs 68 } 69 errors := make([]error, 0, 1) 70 71 var bidderExt adapters.ExtImpBidder 72 err = json.Unmarshal(request.Imp[0].Ext, &bidderExt) 73 74 if err != nil { 75 err = &errortypes.BadInput{ 76 Message: "ext.bidder not provided", 77 } 78 errors = append(errors, err) 79 return nil, errors 80 } 81 var gamoshiExt openrtb_ext.ExtImpGamoshi 82 err = json.Unmarshal(bidderExt.Bidder, &gamoshiExt) 83 if err != nil { 84 err = &errortypes.BadInput{ 85 Message: "ext.bidder.supplyPartnerId not provided", 86 } 87 errors = append(errors, err) 88 return nil, errors 89 } 90 91 if gamoshiExt.SupplyPartnerId == "" { 92 err = &errortypes.BadInput{ 93 Message: "supplyPartnerId is empty", 94 } 95 errors = append(errors, err) 96 return nil, errors 97 } 98 99 thisURI := a.URI 100 if len(thisURI) == 0 { 101 thisURI = "https://rtb.gamoshi.io" 102 } 103 thisURI = thisURI + "/r/" + gamoshiExt.SupplyPartnerId + "/bidr?bidder=prebid-server" 104 headers := http.Header{} 105 headers.Add("Content-Type", "application/json;charset=utf-8") 106 headers.Add("Accept", "application/json") 107 headers.Add("x-openrtb-version", "2.4") 108 109 if request.Device != nil { 110 addHeaderIfNonEmpty(headers, "User-Agent", request.Device.UA) 111 addHeaderIfNonEmpty(headers, "X-Forwarded-For", request.Device.IP) 112 addHeaderIfNonEmpty(headers, "Accept-Language", request.Device.Language) 113 if request.Device.DNT != nil { 114 addHeaderIfNonEmpty(headers, "DNT", strconv.Itoa(int(*request.Device.DNT))) 115 } 116 } 117 118 return []*adapters.RequestData{{ 119 Method: "POST", 120 Uri: thisURI, 121 Body: reqJSON, 122 Headers: headers, 123 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 124 }}, errors 125 } 126 127 func (a *GamoshiAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 128 129 if response.StatusCode == http.StatusNoContent { 130 return nil, nil 131 } 132 133 if response.StatusCode == http.StatusBadRequest { 134 return nil, []error{&errortypes.BadInput{ 135 Message: fmt.Sprintf("Unexpected status code: %d. ", response.StatusCode), 136 }} 137 } 138 139 if response.StatusCode != http.StatusOK { 140 return nil, []error{&errortypes.BadServerResponse{ 141 Message: fmt.Sprintf("unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 142 }} 143 } 144 145 var bidResp openrtb2.BidResponse 146 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 147 return nil, []error{&errortypes.BadServerResponse{ 148 Message: fmt.Sprintf("bad server response: %v. ", err), 149 }} 150 } 151 152 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) 153 sb := bidResp.SeatBid[0] 154 for i := 0; i < len(sb.Bid); i++ { 155 bid := sb.Bid[i] 156 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 157 Bid: &bid, 158 BidType: getMediaType(bid.ImpID, internalRequest.Imp), 159 }) 160 } 161 return bidResponse, nil 162 } 163 164 // Adding header fields to request header 165 func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { 166 if len(headerValue) > 0 { 167 headers.Add(headerName, headerValue) 168 } 169 } 170 171 func getMediaType(impId string, imps []openrtb2.Imp) openrtb_ext.BidType { 172 for _, imp := range imps { 173 if imp.ID == impId { 174 if imp.Video != nil { 175 return openrtb_ext.BidTypeVideo 176 } 177 return openrtb_ext.BidTypeBanner 178 } 179 } 180 return openrtb_ext.BidTypeBanner 181 } 182 183 // Builder builds a new instance of the Gamoshi adapter for the given bidder with the given config. 184 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 185 bidder := &GamoshiAdapter{ 186 URI: config.Endpoint, 187 } 188 return bidder, nil 189 }