Skip to content

Commit 82d16f3

Browse files
Peiling-Dingshunj-nb
authored andcommitted
MSP-change-till-2025-06-24
Load Bidders through Plugins (#50) * Load Bidders through Plugins * Separate core and msp bidders add adapter nova (#51) enforce fb bid floor (#52) fix floor check bug (#53) Add adapters to support facebook multicall (#54) Co-authored-by: Peiling Ding <[email protected]> Add nova multicall (#55) Co-authored-by: Peiling Ding <[email protected]> Load bidder based on so_path (#57) fb dyn config (#60) increase prometheus histogram buckets (#62) Integrating Prometheus Metrics on MSP side (#64) * Integrating Prometheus Metrics on MSP side * Fixing error message --------- Co-authored-by: Muhammad Ashhad Alam <[email protected]@Muhammads-MacBook-Pro.local> add config for scylla access for nova (#70) Signed-off-by: YifengW86 <[email protected]> add fb app security id (#71) mspai-config (#77) implement file db (#76) * implement file and db for prebid-server * move new functions to a new file fix msp analytics add Moloco (#87) fix fb auth id Add Moloco Native For Multicall (#89)
1 parent f358e24 commit 82d16f3

File tree

22 files changed

+508
-33
lines changed

22 files changed

+508
-33
lines changed

adapters/audienceNetwork/facebook.go

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,15 @@ type facebookAdMarkup struct {
3838
}
3939

4040
type facebookReqExt struct {
41-
PlatformID string `json:"platformid"`
42-
AuthID string `json:"authentication_id"`
41+
PlatformID string `json:"platformid"`
42+
AuthID string `json:"authentication_id"`
43+
SecurityAppID string `json:"security_app_id,omitempty"`
44+
}
45+
46+
type ExtImpFB struct {
47+
AppSecret string `json:"app_secret"`
48+
PlatformID string `json:"platform_id"`
49+
SecurityAppID string `json:"security_app_id"`
4350
}
4451

4552
func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
@@ -118,8 +125,9 @@ func (a *adapter) buildRequests(request *openrtb2.BidRequest) ([]*adapters.Reque
118125

119126
// The authentication ID is a sha256 hmac hash encoded as a hex string, based on
120127
// the app secret and the ID of the bid request
121-
func (a *adapter) makeAuthID(req *openrtb2.BidRequest) string {
122-
h := hmac.New(sha256.New, []byte(a.appSecret))
128+
129+
func (a *adapter) makeAuthID(req *openrtb2.BidRequest, appSecret string) string {
130+
h := hmac.New(sha256.New, []byte(appSecret))
123131
h.Write([]byte(req.ID))
124132

125133
return hex.EncodeToString(h.Sum(nil))
@@ -141,9 +149,26 @@ func (a *adapter) modifyRequest(out *openrtb2.BidRequest) error {
141149
// ID *BEFORE* we generate the auth ID since its a hash based on the request ID
142150
out.ID = imp.ID
143151

152+
platformId := a.platformID
153+
appSecret := a.appSecret
154+
securityAppId := ""
155+
156+
var bidderExt adapters.ExtImpBidder
157+
err = json.Unmarshal(imp.Ext, &bidderExt)
158+
if err == nil {
159+
var impressionExt ExtImpFB
160+
err = json.Unmarshal(bidderExt.Bidder, &impressionExt)
161+
if err == nil && len(impressionExt.PlatformID) > 0 && len(impressionExt.AppSecret) > 0 && len(impressionExt.SecurityAppID) > 0 {
162+
platformId = impressionExt.PlatformID
163+
appSecret = impressionExt.AppSecret
164+
securityAppId = impressionExt.SecurityAppID
165+
}
166+
}
167+
144168
reqExt := facebookReqExt{
145-
PlatformID: a.platformID,
146-
AuthID: a.makeAuthID(out),
169+
PlatformID: platformId,
170+
AuthID: a.makeAuthID(out, appSecret),
171+
SecurityAppID: securityAppId,
147172
}
148173

149174
if out.Ext, err = json.Marshal(reqExt); err != nil {
@@ -371,6 +396,18 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, adapterRequest *adapter
371396
continue
372397
}
373398

399+
// TODO: move to msp repo and apply for all bidders
400+
var fbreq openrtb2.BidRequest
401+
err := json.Unmarshal(adapterRequest.Body, &fbreq)
402+
if err == nil {
403+
if len(fbreq.Imp) > 0 && fbreq.Imp[0].BidFloor > bid.Price {
404+
errs = append(errs, &errortypes.BadServerResponse{
405+
Message: fmt.Sprintf("bid price %f less than floor %f", bid.Price, fbreq.Imp[0].BidFloor),
406+
})
407+
continue
408+
}
409+
}
410+
374411
bid.AdID = obj.BidID
375412
bid.CrID = obj.BidID
376413

@@ -454,7 +491,13 @@ func (a *adapter) MakeTimeoutNotification(req *adapters.RequestData) (*adapters.
454491
return &adapters.RequestData{}, errors.New("path app.publisher.id not found in the request")
455492
}
456493

457-
uri := fmt.Sprintf("https://www.facebook.com/audiencenetwork/nurl/?partner=%s&app=%s&auction=%s&ortb_loss_code=2", a.platformID, pubID, rID)
494+
platformId := a.platformID
495+
requestPlatformId, err := jsonparser.GetString(req.Body, "ext", "platformid")
496+
if err == nil {
497+
platformId = requestPlatformId
498+
}
499+
500+
uri := fmt.Sprintf("https://www.facebook.com/audiencenetwork/nurl/?partner=%s&app=%s&auction=%s&ortb_loss_code=2", platformId, pubID, rID)
458501
timeoutReq := adapters.RequestData{
459502
Method: "GET",
460503
Uri: uri,

analytics/build/build.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ func New(analytics *config.Analytics) analytics.Runner {
4444
}
4545
}
4646

47+
customAdapters := mspLoadAnalyticsAdapterPlugins(analytics.Custom)
48+
for adapterName, adapter := range customAdapters {
49+
modules[adapterName] = adapter
50+
}
51+
4752
if analytics.Agma.Enabled {
4853
agmaModule, err := agma.NewModule(
4954
clients.GetDefaultHttpInstance(),

analytics/build/msp_plugin.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package build
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/golang/glog"
8+
"github.com/prebid/prebid-server/v3/analytics"
9+
mspPlugin "github.com/prebid/prebid-server/v3/msp/plugin"
10+
)
11+
12+
type PluginBuilder interface {
13+
Build(json.RawMessage) (analytics.Module, error)
14+
}
15+
16+
func mspLoadAnalyticsAdapterPlugins(cfg map[string]interface{}) enabledAnalytics {
17+
plugins := make(enabledAnalytics, 0)
18+
for name, cfgData := range cfg {
19+
builder, cfgJson, skip, err := mspPlugin.LoadBuilder[PluginBuilder](name, cfgData)
20+
21+
if skip {
22+
continue
23+
}
24+
25+
if err != nil {
26+
panic(err)
27+
}
28+
29+
plugin, err := builder.Build(cfgJson)
30+
if err != nil {
31+
panic(fmt.Sprintf("Failed to build Analytics Adapter plugin %s, error: %+v\n", name, err))
32+
} else {
33+
glog.Infof("Loaded Analytics Adapter plugin: %s\n", name)
34+
plugins[name] = plugin
35+
}
36+
}
37+
38+
return plugins
39+
}

config/adapter.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@ type Adapter struct {
1212

1313
// nededed for Facebook
1414
AppSecret string
15+
16+
// needed for Nova
17+
NovaScylla AdapterNovaScylla
1518
}

config/bidderinfo.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,14 @@ type BidderInfo struct {
4848
PlatformID string `yaml:"platform_id" mapstructure:"platform_id"`
4949
AppSecret string `yaml:"app_secret" mapstructure:"app_secret"`
5050
// EndpointCompression determines, if set, the type of compression the bid request will undergo before being sent to the corresponding bid server
51+
5152
EndpointCompression string `yaml:"endpointCompression" mapstructure:"endpointCompression"`
53+
54+
// For MSP Plugin extension only.
55+
MspSoPath string `mapstructure:"so_path,omitempty" json:"so_path,omitempty"`
56+
57+
// For Nova Adapter only
58+
NovaScylla AdapterNovaScylla `yaml:"scylla" mapstructure:"scylla"`
5259
}
5360

5461
type aliasNillableFields struct {
@@ -235,6 +242,13 @@ type SyncerEndpoint struct {
235242
UserMacro string `yaml:"userMacro" mapstructure:"user_macro"`
236243
}
237244

245+
// AdapterNovaScylla specifies the the scylla config for Nova Adapter
246+
type AdapterNovaScylla struct {
247+
Cluster string `yaml:"cluster" mapstructure:"cluster"`
248+
KeySpace string `yaml:"keyspace" mapstructure:"keyspace"`
249+
TimeOut int32 `yaml:"query_timeout" mapstructure:"query_timeout"`
250+
}
251+
238252
func (s *SyncerEndpoint) Equal(other *SyncerEndpoint) bool {
239253
if s == nil && other == nil {
240254
return true
@@ -705,6 +719,7 @@ func applyBidderInfoConfigOverrides(configBidderInfos nillableFieldBidderInfos,
705719
if !exists {
706720
return nil, fmt.Errorf("error setting configuration for bidder %s: unknown bidder", bidderName)
707721
}
722+
708723
fsBidderInfo, exists := fsBidderInfos[string(normalizedBidderName)]
709724
if !exists {
710725
return nil, fmt.Errorf("error finding configuration for bidder %s: unknown bidder", bidderName)
@@ -757,6 +772,19 @@ func applyBidderInfoConfigOverrides(configBidderInfos nillableFieldBidderInfos,
757772
if configBidderInfo.bidderInfo.EndpointCompression != "" {
758773
mergedBidderInfo.EndpointCompression = configBidderInfo.bidderInfo.EndpointCompression
759774
}
775+
if configBidderInfo.bidderInfo.MspSoPath != "" {
776+
mergedBidderInfo.MspSoPath = configBidderInfo.bidderInfo.MspSoPath
777+
}
778+
if configBidderInfo.bidderInfo.NovaScylla.Cluster != "" {
779+
mergedBidderInfo.NovaScylla.Cluster = configBidderInfo.bidderInfo.NovaScylla.Cluster
780+
}
781+
if configBidderInfo.bidderInfo.NovaScylla.KeySpace != "" {
782+
mergedBidderInfo.NovaScylla.KeySpace = configBidderInfo.bidderInfo.NovaScylla.KeySpace
783+
}
784+
if configBidderInfo.bidderInfo.NovaScylla.TimeOut != 0 {
785+
mergedBidderInfo.NovaScylla.TimeOut = configBidderInfo.bidderInfo.NovaScylla.TimeOut
786+
}
787+
760788
if configBidderInfo.bidderInfo.OpenRTB != nil {
761789
mergedBidderInfo.OpenRTB = configBidderInfo.bidderInfo.OpenRTB
762790
}

config/config.go

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,25 @@ type Configuration struct {
3636
GarbageCollectorThreshold int `mapstructure:"garbage_collector_threshold"`
3737
// StatusResponse is the string which will be returned by the /status endpoint when things are OK.
3838
// If empty, it will return a 204 with no content.
39-
StatusResponse string `mapstructure:"status_response"`
40-
AuctionTimeouts AuctionTimeouts `mapstructure:"auction_timeouts_ms"`
41-
TmaxAdjustments TmaxAdjustments `mapstructure:"tmax_adjustments"`
42-
TmaxDefault int `mapstructure:"tmax_default"`
43-
CacheURL Cache `mapstructure:"cache"`
44-
ExtCacheURL ExternalCache `mapstructure:"external_cache"`
45-
RecaptchaSecret string `mapstructure:"recaptcha_secret"`
46-
HostCookie HostCookie `mapstructure:"host_cookie"`
47-
Metrics Metrics `mapstructure:"metrics"`
48-
StoredRequests StoredRequests `mapstructure:"stored_requests"`
49-
StoredRequestsAMP StoredRequests `mapstructure:"stored_amp_req"`
50-
CategoryMapping StoredRequests `mapstructure:"category_mapping"`
51-
VTrack VTrack `mapstructure:"vtrack"`
52-
Event Event `mapstructure:"event"`
53-
Accounts StoredRequests `mapstructure:"accounts"`
54-
UserSync UserSync `mapstructure:"user_sync"`
39+
40+
StatusResponse string `mapstructure:"status_response"`
41+
AuctionTimeouts AuctionTimeouts `mapstructure:"auction_timeouts_ms"`
42+
TmaxAdjustments TmaxAdjustments `mapstructure:"tmax_adjustments"`
43+
TmaxDefault int `mapstructure:"tmax_default"`
44+
CacheURL Cache `mapstructure:"cache"`
45+
ExtCacheURL ExternalCache `mapstructure:"external_cache"`
46+
RecaptchaSecret string `mapstructure:"recaptcha_secret"`
47+
HostCookie HostCookie `mapstructure:"host_cookie"`
48+
Metrics Metrics `mapstructure:"metrics"`
49+
MSPMetricsConfig MSPMetricsConfig `mapstructure:"msp_metrics"`
50+
StoredRequests StoredRequests `mapstructure:"stored_requests"`
51+
StoredRequestsAMP StoredRequests `mapstructure:"stored_amp_req"`
52+
CategoryMapping StoredRequests `mapstructure:"category_mapping"`
53+
VTrack VTrack `mapstructure:"vtrack"`
54+
Event Event `mapstructure:"event"`
55+
Accounts StoredRequests `mapstructure:"accounts"`
56+
UserSync UserSync `mapstructure:"user_sync"`
57+
5558
// Note that StoredVideo refers to stored video requests, and has nothing to do with caching video creatives.
5659
StoredVideo StoredRequests `mapstructure:"stored_video_req"`
5760
StoredResponses StoredRequests `mapstructure:"stored_responses"`
@@ -479,6 +482,10 @@ type Analytics struct {
479482
File FileLogs `mapstructure:"file"`
480483
Agma AgmaAnalytics `mapstructure:"agma"`
481484
Pubstack Pubstack `mapstructure:"pubstack"`
485+
486+
// Made by MSP. This field is for loading analytics adapter
487+
// dynamically through Golang Plugin at runtime
488+
Custom map[string]interface{} `mapstructure:"custom"`
482489
}
483490

484491
type CurrencyConverter struct {
@@ -579,6 +586,12 @@ type Metrics struct {
579586
Disabled DisabledMetrics `mapstructure:"disabled_metrics"`
580587
}
581588

589+
type MSPMetricsConfig struct {
590+
Enabled bool `mapstructure:"enabled"`
591+
Port int `mapstructure:"port"`
592+
SoPath string `mapstructure:"so_path"`
593+
}
594+
582595
type DisabledMetrics struct {
583596
// True if we want to stop collecting account-to-adapter metrics
584597
AccountAdapterDetails bool `mapstructure:"account_adapter_details"`

endpoints/openrtb2/auction.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,20 @@ func getJsonSyntaxError(testJSON []byte) (bool, string) {
16571657
}
16581658

16591659
func (deps *endpointDeps) getStoredRequests(ctx context.Context, requestJson []byte, impInfo []ImpExtPrebidData) (string, bool, map[string]json.RawMessage, map[string]json.RawMessage, []error) {
1660+
if isDbFetched(requestJson) {
1661+
dbReqMap, dbImpMap, parseErrs := parseDbStoredMaps(requestJson)
1662+
if len(parseErrs) > 0 {
1663+
return "", false, nil, nil, parseErrs
1664+
}
1665+
1666+
// same parameters as normal
1667+
storedBidRequestId, hasStoredBidRequest, err := getStoredRequestId(requestJson)
1668+
if err != nil {
1669+
return "", false, nil, nil, []error{err}
1670+
}
1671+
1672+
return storedBidRequestId, hasStoredBidRequest, dbReqMap, dbImpMap, nil
1673+
}
16601674
// Parse the Stored Request IDs from the BidRequest and Imps.
16611675
storedBidRequestId, hasStoredBidRequest, err := getStoredRequestId(requestJson)
16621676
if err != nil {

endpoints/openrtb2/db_helper.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package openrtb2
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/buger/jsonparser"
8+
)
9+
10+
func isDbFetched(requestJson []byte) bool {
11+
val, dataType, _, err := jsonparser.Get(requestJson, "ext", "db_fetched")
12+
if err != nil {
13+
return false
14+
}
15+
if dataType == jsonparser.Boolean && string(val) == "true" {
16+
return true
17+
}
18+
return false
19+
}
20+
21+
func parseDbStoredMaps(requestJson []byte) (map[string]json.RawMessage, map[string]json.RawMessage, []error) {
22+
var errs []error
23+
storedReqMap := make(map[string]json.RawMessage)
24+
storedImpMap := make(map[string]json.RawMessage)
25+
26+
rawReqs, dt, _, err := jsonparser.Get(requestJson, "ext", "db_storedrequests")
27+
if err == nil && dt == jsonparser.Object {
28+
if e := json.Unmarshal(rawReqs, &storedReqMap); e != nil {
29+
errs = append(errs, fmt.Errorf("failed to unmarshal db_storedrequests: %v", e))
30+
}
31+
}
32+
33+
rawImps, dt2, _, err2 := jsonparser.Get(requestJson, "ext", "db_storedimps")
34+
if err2 == nil && dt2 == jsonparser.Object {
35+
if e := json.Unmarshal(rawImps, &storedImpMap); e != nil {
36+
errs = append(errs, fmt.Errorf("failed to unmarshal db_storedimps: %v", e))
37+
}
38+
}
39+
40+
return storedReqMap, storedImpMap, errs
41+
}

exchange/adapter_util.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import (
1313

1414
func BuildAdapters(client *http.Client, cfg *config.Configuration, infos config.BidderInfos, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]AdaptedBidder, map[openrtb_ext.BidderName]struct{}, []error) {
1515
server := config.Server{ExternalUrl: cfg.ExternalURL, GvlID: cfg.GDPR.HostVendorID, DataCenter: cfg.DataCenter}
16-
bidders, singleFormatBidders, errs := buildBidders(infos, newAdapterBuilders(), server)
16+
adapterBuilders := mspAddAdaptersFromPlugins(newAdapterBuilders(), infos)
17+
bidders, singleFormatBidders, errs := buildBidders(infos, adapterBuilders, server)
1718

1819
if len(errs) > 0 {
1920
return nil, nil, errs
@@ -92,6 +93,7 @@ func buildAdapterInfo(bidderInfo config.BidderInfo) config.Adapter {
9293
adapter.PlatformID = bidderInfo.PlatformID
9394
adapter.AppSecret = bidderInfo.AppSecret
9495
adapter.XAPI = bidderInfo.XAPI
96+
adapter.NovaScylla = bidderInfo.NovaScylla
9597
return adapter
9698
}
9799

0 commit comments

Comments
 (0)