github.com/prebid/prebid-server@v0.275.0/adapters/adquery/adquery.go (about)

     1  package adquery
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"regexp"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/prebid/openrtb/v19/openrtb2"
    12  	"github.com/prebid/prebid-server/adapters"
    13  	"github.com/prebid/prebid-server/config"
    14  	"github.com/prebid/prebid-server/errortypes"
    15  	"github.com/prebid/prebid-server/openrtb_ext"
    16  )
    17  
    18  const (
    19  	defaultCurrency string = "PLN"
    20  	bidderName      string = "adquery"
    21  	prebidVersion   string = "server"
    22  )
    23  
    24  type adapter struct {
    25  	endpoint string
    26  }
    27  
    28  // Builder builds a new instance of the Adquery adapter for the given bidder with the given config.
    29  func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) {
    30  	bidder := &adapter{
    31  		endpoint: config.Endpoint,
    32  	}
    33  	return bidder, nil
    34  }
    35  
    36  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    37  	headers := http.Header{}
    38  	headers.Add("Content-Type", "application/json;charset=utf-8")
    39  	headers.Add("Accept", "application/json")
    40  	headers.Add("x-openrtb-version", "2.5")
    41  
    42  	var result []*adapters.RequestData
    43  	var errs []error
    44  	for _, imp := range request.Imp {
    45  		ext, err := parseExt(imp.Ext)
    46  		if err != nil {
    47  			errs = append(errs, &errortypes.BadInput{err.Error()})
    48  			continue
    49  		}
    50  
    51  		requestJSON, err := json.Marshal(buildRequest(request, &imp, ext))
    52  		if err != nil {
    53  			return nil, append(errs, err)
    54  		}
    55  
    56  		data := &adapters.RequestData{
    57  			Method:  "POST",
    58  			Uri:     a.endpoint,
    59  			Body:    requestJSON,
    60  			Headers: headers,
    61  		}
    62  		result = append(result, data)
    63  	}
    64  
    65  	return result, errs
    66  }
    67  
    68  func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
    69  	if adapters.IsResponseStatusCodeNoContent(responseData) {
    70  		return nil, nil
    71  	}
    72  
    73  	err := adapters.CheckResponseStatusCodeForErrors(responseData)
    74  	if err != nil {
    75  		return nil, []error{err}
    76  	}
    77  
    78  	respData, price, width, height, errs := parseResponseJson(responseData.Body)
    79  	if len(errs) > 0 {
    80  		return nil, errs
    81  	}
    82  
    83  	if respData == nil {
    84  		return adapters.NewBidderResponse(), nil
    85  	}
    86  
    87  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
    88  	if respData.Currency != "" {
    89  		bidResponse.Currency = respData.Currency
    90  	} else {
    91  		bidResponse.Currency = defaultCurrency
    92  	}
    93  
    94  	var bidReqIdRegex = regexp.MustCompile(`^` + request.ID)
    95  	impId := bidReqIdRegex.ReplaceAllLiteralString(respData.ReqID, "")
    96  
    97  	bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
    98  		Bid: &openrtb2.Bid{
    99  			// There's much more possible fields to be added here, see OpenRTB docs for reference (type: Bid)
   100  			ID:      respData.ReqID,
   101  			ImpID:   impId,
   102  			Price:   price,
   103  			AdM:     fmt.Sprintf("<script src=\"%s\"></script>%s", respData.AdQLib, respData.Tag),
   104  			ADomain: respData.ADomains,
   105  			CrID:    fmt.Sprintf("%d", respData.CrID),
   106  			W:       width,
   107  			H:       height,
   108  		},
   109  		BidType: respData.MediaType.Name,
   110  	})
   111  
   112  	return bidResponse, nil
   113  }
   114  
   115  func buildRequest(bidReq *openrtb2.BidRequest, imp *openrtb2.Imp, ext *openrtb_ext.ImpExtAdQuery) *BidderRequest {
   116  	userId := ""
   117  	if bidReq.User != nil {
   118  		userId = bidReq.User.ID
   119  	}
   120  
   121  	return &BidderRequest{
   122  		V:                   prebidVersion,
   123  		PlacementCode:       ext.PlacementID,
   124  		AuctionId:           "",
   125  		BidType:             ext.Type,
   126  		AdUnitCode:          imp.TagID,
   127  		BidQid:              userId,
   128  		BidId:               fmt.Sprintf("%s%s", bidReq.ID, imp.ID),
   129  		Bidder:              bidderName,
   130  		BidderRequestId:     bidReq.ID,
   131  		BidRequestsCount:    1,
   132  		BidderRequestsCount: 1,
   133  		Sizes:               getImpSizes(imp),
   134  	}
   135  }
   136  
   137  func parseExt(ext json.RawMessage) (*openrtb_ext.ImpExtAdQuery, error) {
   138  	var bext adapters.ExtImpBidder
   139  	err := json.Unmarshal(ext, &bext)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	var adsExt openrtb_ext.ImpExtAdQuery
   145  	err = json.Unmarshal(bext.Bidder, &adsExt)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	// not validating, because it should have been done earlier by the server
   151  	return &adsExt, nil
   152  }
   153  
   154  func parseResponseJson(respBody []byte) (*ResponseData, float64, int64, int64, []error) {
   155  	var response ResponseAdQuery
   156  	if err := json.Unmarshal(respBody, &response); err != nil {
   157  		return nil, 0, 0, 0, []error{err}
   158  	}
   159  
   160  	if response.Data == nil {
   161  		return nil, 0, 0, 0, nil
   162  	}
   163  
   164  	var errs []error
   165  	price, err := strconv.ParseFloat(response.Data.CPM, 64)
   166  	if err != nil {
   167  		errs = append(errs, err)
   168  	}
   169  	width, err := strconv.ParseInt(response.Data.MediaType.Width, 10, 64)
   170  	if err != nil {
   171  		errs = append(errs, err)
   172  	}
   173  	height, err := strconv.ParseInt(response.Data.MediaType.Height, 10, 64)
   174  	if err != nil {
   175  		errs = append(errs, err)
   176  	}
   177  
   178  	if response.Data.MediaType.Name != openrtb_ext.BidTypeBanner {
   179  		return nil, 0, 0, 0, []error{fmt.Errorf("unsupported MediaType: %s", response.Data.MediaType.Name)}
   180  	}
   181  
   182  	if len(errs) > 0 {
   183  		return nil, 0, 0, 0, errs
   184  	}
   185  	return response.Data, price, width, height, nil
   186  }
   187  
   188  func getImpSizes(imp *openrtb2.Imp) string {
   189  	if imp.Banner == nil {
   190  		return ""
   191  	}
   192  
   193  	if len(imp.Banner.Format) > 0 {
   194  		sizes := make([]string, len(imp.Banner.Format))
   195  		for i, format := range imp.Banner.Format {
   196  			sizes[i] = strconv.FormatInt(format.W, 10) + "x" + strconv.FormatInt(format.H, 10)
   197  		}
   198  
   199  		return strings.Join(sizes, ",")
   200  	}
   201  
   202  	if imp.Banner.W != nil && imp.Banner.H != nil {
   203  		return strconv.FormatInt(*imp.Banner.W, 10) + "x" + strconv.FormatInt(*imp.Banner.H, 10)
   204  	}
   205  
   206  	return ""
   207  }