github.com/prebid/prebid-server/v2@v2.18.0/usersync/syncersbuilder.go (about) 1 package usersync 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/prebid/prebid-server/v2/config" 9 ) 10 11 type namedSyncerConfig struct { 12 name string 13 cfg config.Syncer 14 } 15 16 // SyncerBuildError represents an error with building a syncer. 17 type SyncerBuildError struct { 18 Bidder string 19 SyncerKey string 20 Err error 21 } 22 23 // Error implements the standard error interface. 24 func (e SyncerBuildError) Error() string { 25 return fmt.Sprintf("cannot create syncer for bidder %s with key %s: %v", e.Bidder, e.SyncerKey, e.Err) 26 } 27 28 func BuildSyncers(hostConfig *config.Configuration, bidderInfos config.BidderInfos) (map[string]Syncer, []error) { 29 // map syncer config by bidder 30 cfgByBidder := make(map[string]config.Syncer, len(bidderInfos)) 31 for bidder, cfg := range bidderInfos { 32 if shouldCreateSyncer(cfg) { 33 cfgByBidder[bidder] = *cfg.Syncer 34 } 35 } 36 37 // map syncer config by key 38 cfgBySyncerKey := make(map[string][]namedSyncerConfig, len(bidderInfos)) 39 for bidder, cfg := range cfgByBidder { 40 if cfg.Key == "" { 41 cfg.Key = bidder 42 } 43 cfgBySyncerKey[cfg.Key] = append(cfgBySyncerKey[cfg.Key], namedSyncerConfig{bidder, cfg}) 44 } 45 46 // resolve host endpoint 47 hostUserSyncConfig := hostConfig.UserSync 48 if hostUserSyncConfig.ExternalURL == "" { 49 hostUserSyncConfig.ExternalURL = hostConfig.ExternalURL 50 } 51 52 // create syncers 53 errs := []error{} 54 syncers := make(map[string]Syncer, len(bidderInfos)) 55 for key, cfgGroup := range cfgBySyncerKey { 56 primaryCfg, err := chooseSyncerConfig(cfgGroup) 57 if err != nil { 58 errs = append(errs, err) 59 continue 60 } 61 62 for _, bidder := range cfgGroup { 63 syncer, err := NewSyncer(hostUserSyncConfig, primaryCfg.cfg, bidder.name) 64 if err != nil { 65 errs = append(errs, SyncerBuildError{ 66 Bidder: primaryCfg.name, 67 SyncerKey: key, 68 Err: err, 69 }) 70 continue 71 } 72 syncers[bidder.name] = syncer 73 } 74 } 75 76 if len(errs) > 0 { 77 return nil, errs 78 } 79 return syncers, nil 80 } 81 82 func shouldCreateSyncer(cfg config.BidderInfo) bool { 83 if cfg.Disabled { 84 return false 85 } 86 87 if cfg.Syncer == nil { 88 return false 89 } 90 91 // a syncer may provide just a Supports field to provide hints to the host. we should only try to create a syncer 92 // if there is at least one non-Supports value populated. 93 return cfg.Syncer.Key != "" || cfg.Syncer.IFrame != nil || cfg.Syncer.Redirect != nil || cfg.Syncer.SupportCORS != nil 94 } 95 96 func chooseSyncerConfig(biddersSyncerConfig []namedSyncerConfig) (namedSyncerConfig, error) { 97 if len(biddersSyncerConfig) == 1 { 98 return biddersSyncerConfig[0], nil 99 } 100 101 var bidderNames []string 102 var bidderNamesWithEndpoints []string 103 var syncerConfig namedSyncerConfig 104 for _, bidder := range biddersSyncerConfig { 105 bidderNames = append(bidderNames, bidder.name) 106 if bidder.cfg.IFrame != nil || bidder.cfg.Redirect != nil { 107 bidderNamesWithEndpoints = append(bidderNamesWithEndpoints, bidder.name) 108 syncerConfig = bidder 109 } 110 } 111 112 if len(bidderNamesWithEndpoints) == 0 { 113 sort.Strings(bidderNames) 114 bidders := strings.Join(bidderNames, ", ") 115 return namedSyncerConfig{}, fmt.Errorf("bidders %s share the same syncer key, but none define endpoints (iframe and/or redirect)", bidders) 116 } 117 118 if len(bidderNamesWithEndpoints) > 1 { 119 sort.Strings(bidderNamesWithEndpoints) 120 bidders := strings.Join(bidderNamesWithEndpoints, ", ") 121 return namedSyncerConfig{}, fmt.Errorf("bidders %s define endpoints (iframe and/or redirect) for the same syncer key, but only one bidder is permitted to define endpoints", bidders) 122 } 123 124 return syncerConfig, nil 125 }