github.com/prebid/prebid-server@v0.275.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/v19/openrtb2" 10 "github.com/prebid/prebid-server/adapters" 11 "github.com/prebid/prebid-server/config" 12 "github.com/prebid/prebid-server/errortypes" 13 "github.com/prebid/prebid-server/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 }}, errors 124 } 125 126 func (a *GamoshiAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 127 128 if response.StatusCode == http.StatusNoContent { 129 return nil, nil 130 } 131 132 if response.StatusCode == http.StatusBadRequest { 133 return nil, []error{&errortypes.BadInput{ 134 Message: fmt.Sprintf("Unexpected status code: %d. ", response.StatusCode), 135 }} 136 } 137 138 if response.StatusCode != http.StatusOK { 139 return nil, []error{&errortypes.BadServerResponse{ 140 Message: fmt.Sprintf("unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 141 }} 142 } 143 144 var bidResp openrtb2.BidResponse 145 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 146 return nil, []error{&errortypes.BadServerResponse{ 147 Message: fmt.Sprintf("bad server response: %v. ", err), 148 }} 149 } 150 151 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) 152 sb := bidResp.SeatBid[0] 153 for i := 0; i < len(sb.Bid); i++ { 154 bid := sb.Bid[i] 155 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 156 Bid: &bid, 157 BidType: getMediaType(bid.ImpID, internalRequest.Imp), 158 }) 159 } 160 return bidResponse, nil 161 } 162 163 // Adding header fields to request header 164 func addHeaderIfNonEmpty(headers http.Header, headerName string, headerValue string) { 165 if len(headerValue) > 0 { 166 headers.Add(headerName, headerValue) 167 } 168 } 169 170 func getMediaType(impId string, imps []openrtb2.Imp) openrtb_ext.BidType { 171 for _, imp := range imps { 172 if imp.ID == impId { 173 if imp.Video != nil { 174 return openrtb_ext.BidTypeVideo 175 } 176 return openrtb_ext.BidTypeBanner 177 } 178 } 179 return openrtb_ext.BidTypeBanner 180 } 181 182 // Builder builds a new instance of the Gamoshi adapter for the given bidder with the given config. 183 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 184 bidder := &GamoshiAdapter{ 185 URI: config.Endpoint, 186 } 187 return bidder, nil 188 }