github.com/prebid/prebid-server/v2@v2.18.0/endpoints/info/bidders_detail.go (about)

     1  package info
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strings"
     8  
     9  	"github.com/golang/glog"
    10  	"github.com/julienschmidt/httprouter"
    11  	"github.com/prebid/prebid-server/v2/config"
    12  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    13  	"github.com/prebid/prebid-server/v2/util/jsonutil"
    14  )
    15  
    16  const (
    17  	statusActive   string = "ACTIVE"
    18  	statusDisabled string = "DISABLED"
    19  )
    20  
    21  // NewBiddersDetailEndpoint builds a handler for the /info/bidders/<bidder> endpoint.
    22  func NewBiddersDetailEndpoint(bidders config.BidderInfos, aliases map[string]string) httprouter.Handle {
    23  	responses, err := prepareBiddersDetailResponse(bidders, aliases)
    24  	if err != nil {
    25  		glog.Fatalf("error creating /info/bidders/<bidder> endpoint response: %v", err)
    26  	}
    27  
    28  	return func(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) {
    29  		bidder := ps.ByName("bidderName")
    30  
    31  		coreBidderName, found := getNormalisedBidderName(bidder, aliases)
    32  		if !found {
    33  			w.WriteHeader(http.StatusNotFound)
    34  		}
    35  		if response, ok := responses[coreBidderName]; ok {
    36  			w.Header().Set("Content-Type", "application/json")
    37  			if _, err := w.Write(response); err != nil {
    38  				glog.Errorf("error writing response to /info/bidders/%s: %v", bidder, err)
    39  			}
    40  		} else {
    41  			w.WriteHeader(http.StatusNotFound)
    42  		}
    43  	}
    44  }
    45  
    46  func getNormalisedBidderName(bidderName string, aliases map[string]string) (string, bool) {
    47  	if strings.ToLower(bidderName) == "all" {
    48  		return "all", true
    49  	}
    50  	coreBidderName, ok := openrtb_ext.NormalizeBidderName(bidderName)
    51  	if !ok { //check default aliases if not found in coreBidders
    52  		if _, isDefaultAlias := aliases[bidderName]; isDefaultAlias {
    53  			return bidderName, true
    54  		}
    55  		return "", false
    56  	}
    57  	return coreBidderName.String(), true
    58  }
    59  
    60  func prepareBiddersDetailResponse(bidders config.BidderInfos, aliases map[string]string) (map[string][]byte, error) {
    61  	details, err := mapDetails(bidders, aliases)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	responses, err := marshalDetailsResponse(details)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	all, err := marshalAllResponse(responses)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	responses["all"] = all
    76  
    77  	return responses, nil
    78  }
    79  
    80  func mapDetails(bidders config.BidderInfos, aliases map[string]string) (map[string]bidderDetail, error) {
    81  	details := map[string]bidderDetail{}
    82  
    83  	for bidderName, bidderInfo := range bidders {
    84  		details[bidderName] = mapDetailFromConfig(bidderInfo)
    85  	}
    86  
    87  	for aliasName, bidderName := range aliases {
    88  		aliasBaseInfo, aliasBaseInfoFound := details[bidderName]
    89  		if !aliasBaseInfoFound {
    90  			return nil, fmt.Errorf("base adapter %s for alias %s not found", bidderName, aliasName)
    91  		}
    92  
    93  		aliasInfo := aliasBaseInfo
    94  		aliasInfo.AliasOf = bidderName
    95  		details[aliasName] = aliasInfo
    96  	}
    97  
    98  	return details, nil
    99  }
   100  
   101  func marshalDetailsResponse(details map[string]bidderDetail) (map[string][]byte, error) {
   102  	responses := map[string][]byte{}
   103  
   104  	for bidder, detail := range details {
   105  		json, err := jsonutil.Marshal(detail)
   106  		if err != nil {
   107  			return nil, fmt.Errorf("unable to marshal info for bidder %s: %v", bidder, err)
   108  		}
   109  		responses[bidder] = json
   110  	}
   111  
   112  	return responses, nil
   113  }
   114  
   115  func marshalAllResponse(responses map[string][]byte) ([]byte, error) {
   116  	responsesJSON := make(map[string]json.RawMessage, len(responses))
   117  
   118  	for k, v := range responses {
   119  		responsesJSON[k] = json.RawMessage(v)
   120  	}
   121  
   122  	json, err := jsonutil.Marshal(responsesJSON)
   123  	if err != nil {
   124  		return nil, fmt.Errorf("unable to marshal info for bidder all: %v", err)
   125  	}
   126  	return json, nil
   127  }
   128  
   129  type bidderDetail struct {
   130  	Status       string        `json:"status"`
   131  	UsesHTTPS    *bool         `json:"usesHttps,omitempty"`
   132  	Maintainer   *maintainer   `json:"maintainer,omitempty"`
   133  	Capabilities *capabilities `json:"capabilities,omitempty"`
   134  	AliasOf      string        `json:"aliasOf,omitempty"`
   135  }
   136  
   137  type maintainer struct {
   138  	Email string `json:"email"`
   139  }
   140  
   141  type capabilities struct {
   142  	App  *platform `json:"app,omitempty"`
   143  	Site *platform `json:"site,omitempty"`
   144  	DOOH *platform `json:"dooh,omitempty"`
   145  }
   146  
   147  type platform struct {
   148  	MediaTypes []string `json:"mediaTypes"`
   149  }
   150  
   151  func mapDetailFromConfig(c config.BidderInfo) bidderDetail {
   152  	var bidderDetail bidderDetail
   153  
   154  	if c.Maintainer != nil {
   155  		bidderDetail.Maintainer = &maintainer{
   156  			Email: c.Maintainer.Email,
   157  		}
   158  	}
   159  
   160  	if c.IsEnabled() {
   161  		bidderDetail.Status = statusActive
   162  
   163  		usesHTTPS := strings.HasPrefix(strings.ToLower(c.Endpoint), "https://")
   164  		bidderDetail.UsesHTTPS = &usesHTTPS
   165  
   166  		if c.Capabilities != nil {
   167  			bidderDetail.Capabilities = &capabilities{}
   168  
   169  			if c.Capabilities.App != nil {
   170  				bidderDetail.Capabilities.App = &platform{
   171  					MediaTypes: mapMediaTypes(c.Capabilities.App.MediaTypes),
   172  				}
   173  			}
   174  
   175  			if c.Capabilities.Site != nil {
   176  				bidderDetail.Capabilities.Site = &platform{
   177  					MediaTypes: mapMediaTypes(c.Capabilities.Site.MediaTypes),
   178  				}
   179  			}
   180  
   181  			if c.Capabilities.DOOH != nil {
   182  				bidderDetail.Capabilities.DOOH = &platform{
   183  					MediaTypes: mapMediaTypes(c.Capabilities.DOOH.MediaTypes),
   184  				}
   185  			}
   186  		}
   187  	} else {
   188  		bidderDetail.Status = statusDisabled
   189  	}
   190  
   191  	return bidderDetail
   192  }
   193  
   194  func mapMediaTypes(m []openrtb_ext.BidType) []string {
   195  	mediaTypes := make([]string, len(m))
   196  
   197  	for i, v := range m {
   198  		mediaTypes[i] = string(v)
   199  	}
   200  
   201  	return mediaTypes
   202  }