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

     1  package smartadserver
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     8  	"path"
     9  	"strconv"
    10  
    11  	"github.com/prebid/openrtb/v20/openrtb2"
    12  	"github.com/prebid/prebid-server/v2/adapters"
    13  	"github.com/prebid/prebid-server/v2/config"
    14  	"github.com/prebid/prebid-server/v2/errortypes"
    15  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    16  )
    17  
    18  type SmartAdserverAdapter struct {
    19  	host   string
    20  	Server config.Server
    21  }
    22  
    23  // Builder builds a new instance of the SmartAdserver adapter for the given bidder with the given config.
    24  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    25  	bidder := &SmartAdserverAdapter{
    26  		host: config.Endpoint,
    27  	}
    28  	return bidder, nil
    29  }
    30  
    31  // MakeRequests makes the HTTP requests which should be made to fetch bids.
    32  func (a *SmartAdserverAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    33  	if len(request.Imp) == 0 {
    34  		return nil, []error{&errortypes.BadInput{
    35  			Message: "No impression in the bid request",
    36  		}}
    37  	}
    38  
    39  	var adapterRequests []*adapters.RequestData
    40  	var errs []error
    41  
    42  	// We copy the original request.
    43  	smartRequest := *request
    44  
    45  	// We create or copy the Site object.
    46  	if smartRequest.Site == nil {
    47  		smartRequest.Site = &openrtb2.Site{}
    48  	} else {
    49  		site := *smartRequest.Site
    50  		smartRequest.Site = &site
    51  	}
    52  
    53  	// We create or copy the Publisher object.
    54  	if smartRequest.Site.Publisher == nil {
    55  		smartRequest.Site.Publisher = &openrtb2.Publisher{}
    56  	} else {
    57  		publisher := *smartRequest.Site.Publisher
    58  		smartRequest.Site.Publisher = &publisher
    59  	}
    60  
    61  	// We send one serialized "smartRequest" per impression of the original request.
    62  	for _, imp := range request.Imp {
    63  		var bidderExt adapters.ExtImpBidder
    64  		if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
    65  			errs = append(errs, &errortypes.BadInput{
    66  				Message: "Error parsing bidderExt object",
    67  			})
    68  			continue
    69  		}
    70  
    71  		var smartadserverExt openrtb_ext.ExtImpSmartadserver
    72  		if err := json.Unmarshal(bidderExt.Bidder, &smartadserverExt); err != nil {
    73  			errs = append(errs, &errortypes.BadInput{
    74  				Message: "Error parsing smartadserverExt parameters",
    75  			})
    76  			continue
    77  		}
    78  
    79  		// Adding publisher id.
    80  		smartRequest.Site.Publisher.ID = strconv.Itoa(smartadserverExt.NetworkID)
    81  
    82  		// We send one request for each impression.
    83  		smartRequest.Imp = []openrtb2.Imp{imp}
    84  
    85  		var errMarshal error
    86  		if imp.Ext, errMarshal = json.Marshal(smartadserverExt); errMarshal != nil {
    87  			errs = append(errs, &errortypes.BadInput{
    88  				Message: errMarshal.Error(),
    89  			})
    90  			continue
    91  		}
    92  
    93  		reqJSON, err := json.Marshal(smartRequest)
    94  		if err != nil {
    95  			errs = append(errs, &errortypes.BadInput{
    96  				Message: "Error parsing reqJSON object",
    97  			})
    98  			continue
    99  		}
   100  
   101  		url, err := a.BuildEndpointURL(&smartadserverExt)
   102  		if url == "" {
   103  			errs = append(errs, err)
   104  			continue
   105  		}
   106  
   107  		headers := http.Header{}
   108  		headers.Add("Content-Type", "application/json;charset=utf-8")
   109  		headers.Add("Accept", "application/json")
   110  		adapterRequests = append(adapterRequests, &adapters.RequestData{
   111  			Method:  "POST",
   112  			Uri:     url,
   113  			Body:    reqJSON,
   114  			Headers: headers,
   115  			ImpIDs:  openrtb_ext.GetImpIDs(smartRequest.Imp),
   116  		})
   117  	}
   118  	return adapterRequests, errs
   119  }
   120  
   121  // MakeBids unpacks the server's response into Bids.
   122  func (a *SmartAdserverAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   123  	if response.StatusCode == http.StatusNoContent {
   124  		return nil, nil
   125  	}
   126  
   127  	if response.StatusCode == http.StatusBadRequest {
   128  		return nil, []error{&errortypes.BadInput{
   129  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
   130  		}}
   131  	}
   132  
   133  	if response.StatusCode != http.StatusOK {
   134  		return nil, []error{&errortypes.BadServerResponse{
   135  			Message: "Unexpected status code: " + strconv.Itoa(response.StatusCode) + ". Run with request.debug = 1 for more info",
   136  		}}
   137  	}
   138  
   139  	var bidResp openrtb2.BidResponse
   140  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   141  		return nil, []error{err}
   142  	}
   143  
   144  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
   145  
   146  	for _, sb := range bidResp.SeatBid {
   147  		for i := 0; i < len(sb.Bid); i++ {
   148  			bid := sb.Bid[i]
   149  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   150  				Bid:     &bid,
   151  				BidType: getMediaTypeForImp(bid.ImpID, internalRequest.Imp),
   152  			})
   153  
   154  		}
   155  	}
   156  	return bidResponse, []error{}
   157  }
   158  
   159  // BuildEndpointURL : Builds endpoint url
   160  func (a *SmartAdserverAdapter) BuildEndpointURL(params *openrtb_ext.ExtImpSmartadserver) (string, error) {
   161  	uri, err := url.Parse(a.host)
   162  	if err != nil || uri.Scheme == "" || uri.Host == "" {
   163  		return "", &errortypes.BadInput{
   164  			Message: "Malformed URL: " + a.host + ".",
   165  		}
   166  	}
   167  
   168  	uri.Path = path.Join(uri.Path, "api/bid")
   169  	uri.RawQuery = "callerId=5"
   170  
   171  	return uri.String(), nil
   172  }
   173  
   174  func getMediaTypeForImp(impID string, imps []openrtb2.Imp) openrtb_ext.BidType {
   175  	for _, imp := range imps {
   176  		if imp.ID == impID {
   177  			if imp.Video != nil {
   178  				return openrtb_ext.BidTypeVideo
   179  			} else if imp.Native != nil {
   180  				return openrtb_ext.BidTypeNative
   181  			}
   182  			return openrtb_ext.BidTypeBanner
   183  		}
   184  	}
   185  	return openrtb_ext.BidTypeBanner
   186  }