github.com/prebid/prebid-server/v2@v2.18.0/adapters/yahooAds/yahooAds.go (about)

     1  package yahooAds
     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  	"github.com/prebid/prebid-server/v2/util/ptrutil"
    14  )
    15  
    16  type adapter struct {
    17  	URI string
    18  }
    19  
    20  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    21  	var errors []error
    22  
    23  	reqs := make([]*adapters.RequestData, 0, len(request.Imp))
    24  	headers := http.Header{}
    25  
    26  	headers.Add("Content-Type", "application/json;charset=utf-8")
    27  	headers.Add("Accept", "application/json")
    28  	headers.Add("x-openrtb-version", "2.5")
    29  
    30  	if request.Device != nil && request.Device.UA != "" {
    31  		headers.Set("User-Agent", request.Device.UA)
    32  	}
    33  
    34  	for idx, imp := range request.Imp {
    35  		var bidderExt adapters.ExtImpBidder
    36  		err := json.Unmarshal(imp.Ext, &bidderExt)
    37  		if err != nil {
    38  			err = &errortypes.BadInput{
    39  				Message: fmt.Sprintf("imp #%d: ext.bidder not provided", idx),
    40  			}
    41  			errors = append(errors, err)
    42  			continue
    43  		}
    44  
    45  		var yahooAdsExt openrtb_ext.ExtImpYahooAds
    46  		err = json.Unmarshal(bidderExt.Bidder, &yahooAdsExt)
    47  		if err != nil {
    48  			err = &errortypes.BadInput{
    49  				Message: fmt.Sprintf("imp #%d: %s", idx, err.Error()),
    50  			}
    51  			errors = append(errors, err)
    52  			continue
    53  		}
    54  
    55  		// Split up multi-impression requests into multiple requests so that
    56  		// each split request is only associated to a single impression
    57  		reqCopy := *request
    58  		reqCopy.Imp = []openrtb2.Imp{imp}
    59  
    60  		if request.Site != nil {
    61  			siteCopy := *request.Site
    62  			reqCopy.Site = &siteCopy
    63  		} else if request.App != nil {
    64  			appCopy := *request.App
    65  			reqCopy.App = &appCopy
    66  		}
    67  
    68  		if err := changeRequestForBidService(&reqCopy, &yahooAdsExt); err != nil {
    69  			errors = append(errors, err)
    70  			continue
    71  		}
    72  
    73  		reqJSON, err := json.Marshal(&reqCopy)
    74  		if err != nil {
    75  			errors = append(errors, err)
    76  			continue
    77  		}
    78  
    79  		reqs = append(reqs, &adapters.RequestData{
    80  			Method:  "POST",
    81  			Uri:     a.URI,
    82  			Body:    reqJSON,
    83  			Headers: headers,
    84  			ImpIDs:  openrtb_ext.GetImpIDs(reqCopy.Imp),
    85  		})
    86  	}
    87  
    88  	return reqs, errors
    89  }
    90  
    91  func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
    92  
    93  	if response.StatusCode == http.StatusNoContent {
    94  		return nil, nil
    95  	}
    96  
    97  	if response.StatusCode != http.StatusOK {
    98  		return nil, []error{&errortypes.BadServerResponse{
    99  			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
   100  		}}
   101  	}
   102  
   103  	var bidResp openrtb2.BidResponse
   104  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   105  		return nil, []error{&errortypes.BadServerResponse{
   106  			Message: fmt.Sprintf("Bad server response: %d.", err),
   107  		}}
   108  	}
   109  
   110  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(internalRequest.Imp))
   111  
   112  	for _, sb := range bidResp.SeatBid {
   113  		for _, bid := range sb.Bid {
   114  			bid := bid
   115  			exists, mediaTypeId := getImpInfo(bid.ImpID, internalRequest.Imp)
   116  			if !exists {
   117  				return nil, []error{&errortypes.BadServerResponse{
   118  					Message: fmt.Sprintf("Unknown ad unit code '%s'", bid.ImpID),
   119  				}}
   120  			}
   121  
   122  			if openrtb_ext.BidTypeBanner != mediaTypeId &&
   123  				openrtb_ext.BidTypeVideo != mediaTypeId {
   124  				//only banner and video are mediaTypeId, anything else is ignored
   125  				continue
   126  			}
   127  
   128  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   129  				Bid:     &bid,
   130  				BidType: mediaTypeId,
   131  			})
   132  		}
   133  	}
   134  
   135  	return bidResponse, nil
   136  }
   137  
   138  func getImpInfo(impId string, imps []openrtb2.Imp) (bool, openrtb_ext.BidType) {
   139  	var mediaType openrtb_ext.BidType
   140  	var exists bool
   141  	for _, imp := range imps {
   142  		if imp.ID == impId {
   143  			exists = true
   144  			if imp.Banner != nil {
   145  				mediaType = openrtb_ext.BidTypeBanner
   146  			} else if imp.Video != nil {
   147  				mediaType = openrtb_ext.BidTypeVideo
   148  			}
   149  			break
   150  		}
   151  	}
   152  	return exists, mediaType
   153  }
   154  
   155  func changeRequestForBidService(request *openrtb2.BidRequest, extension *openrtb_ext.ExtImpYahooAds) error {
   156  	/* Always override the tag ID and (site ID or app ID) of the request */
   157  	request.Imp[0].TagID = extension.Pos
   158  	if request.Site != nil {
   159  		request.Site.ID = extension.Dcn
   160  	} else if request.App != nil {
   161  		request.App.ID = extension.Dcn
   162  	}
   163  
   164  	if request.Imp[0].Banner != nil {
   165  		banner := *request.Imp[0].Banner
   166  		request.Imp[0].Banner = &banner
   167  
   168  		err := validateBanner(&banner)
   169  		if err != nil {
   170  			return err
   171  		}
   172  	}
   173  
   174  	if request.Regs != nil && request.Regs.GPP != "" {
   175  		requestRegs := *request.Regs
   176  		if requestRegs.Ext == nil {
   177  			requestRegs.Ext = json.RawMessage("{}")
   178  		}
   179  		var regsExt map[string]json.RawMessage
   180  		err := json.Unmarshal(requestRegs.Ext, &regsExt)
   181  		if err != nil {
   182  			return err
   183  		}
   184  		regsExt["gpp"], err = json.Marshal(&requestRegs.GPP)
   185  
   186  		if requestRegs.GPPSID != nil {
   187  			regsExt["gpp_sid"], err = json.Marshal(&requestRegs.GPPSID)
   188  			if err != nil {
   189  				return err
   190  			}
   191  		}
   192  
   193  		requestRegs.Ext, err = json.Marshal(&regsExt)
   194  		if err != nil {
   195  			return err
   196  		}
   197  		requestRegs.GPP = ""
   198  		requestRegs.GPPSID = nil
   199  		request.Regs = &requestRegs
   200  	}
   201  
   202  	return nil
   203  }
   204  
   205  func validateBanner(banner *openrtb2.Banner) error {
   206  	if banner.W != nil && banner.H != nil {
   207  		if *banner.W == 0 || *banner.H == 0 {
   208  			return fmt.Errorf("Invalid sizes provided for Banner %dx%d", *banner.W, *banner.H)
   209  		}
   210  		return nil
   211  	}
   212  
   213  	if len(banner.Format) == 0 {
   214  		return fmt.Errorf("No sizes provided for Banner %v", banner.Format)
   215  	}
   216  
   217  	banner.W = ptrutil.ToPtr(banner.Format[0].W)
   218  	banner.H = ptrutil.ToPtr(banner.Format[0].H)
   219  
   220  	return nil
   221  }
   222  
   223  // Builder builds a new instance of the YahooAds adapter for the given bidder with the given config.
   224  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   225  	bidder := &adapter{
   226  		URI: config.Endpoint,
   227  	}
   228  	return bidder, nil
   229  }