Skip to content

Commit e20ff41

Browse files
author
Samuel Alejandro Maldonado Garcia
committed
alvads initial release
1 parent 8cf9c00 commit e20ff41

9 files changed

Lines changed: 589 additions & 0 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package org.prebid.server.bidder.alvads;
2+
3+
import com.iab.openrtb.request.BidRequest;
4+
import com.iab.openrtb.request.Device;
5+
import com.iab.openrtb.request.Imp;
6+
import com.iab.openrtb.request.Regs;
7+
import com.iab.openrtb.request.User;
8+
import org.prebid.server.json.JacksonMapper;
9+
10+
import java.util.Collections;
11+
import java.util.HashMap;
12+
import java.util.List;
13+
import java.util.Map;
14+
15+
public class AlvaRequestORTBBuilder {
16+
17+
private String id;
18+
private List<AlvadsRequestORTB.AlvaAdsImp> imp;
19+
private Device device;
20+
private User user;
21+
private Regs regs;
22+
private AlvadsRequestORTB.AlvaAdsSite site;
23+
private final BidRequest bidRequest;
24+
private final AlvadsImpExt ext;
25+
private final JacksonMapper mapper;
26+
private final Imp bidImp;
27+
28+
public AlvaRequestORTBBuilder(BidRequest bidRequest, Imp imp, AlvadsImpExt ext, JacksonMapper mapper) {
29+
this.bidRequest = bidRequest;
30+
this.bidImp = imp;
31+
this.ext = ext;
32+
this.mapper = mapper;
33+
}
34+
35+
public AlvaRequestORTBBuilder setId(String id) {
36+
this.id = id;
37+
return this;
38+
}
39+
40+
public AlvaRequestORTBBuilder setImp(List<AlvadsRequestORTB.AlvaAdsImp> imp) {
41+
this.imp = imp;
42+
return this;
43+
}
44+
45+
public AlvaRequestORTBBuilder setDevice(Device device) {
46+
this.device = device;
47+
return this;
48+
}
49+
50+
public AlvaRequestORTBBuilder setUser(User user) {
51+
this.user = user;
52+
return this;
53+
}
54+
55+
public AlvaRequestORTBBuilder setRegs(Regs regs) {
56+
this.regs = regs;
57+
return this;
58+
}
59+
60+
public AlvaRequestORTBBuilder setSite(AlvadsRequestORTB.AlvaAdsSite site) {
61+
this.site = site;
62+
return this;
63+
}
64+
65+
public AlvadsRequestORTB build() {
66+
final AlvadsRequestORTB request = new AlvadsRequestORTB();
67+
request.setId(this.id != null ? this.id : this.bidRequest.getId());
68+
if (this.imp == null) {
69+
final AlvadsRequestORTB.AlvaAdsImp alvaAdsImp = new AlvadsRequestORTB.AlvaAdsImp();
70+
alvaAdsImp.setId(this.bidImp.getId());
71+
final Map<String, Object> banner = new HashMap<>();
72+
banner.put("w", bidImp.getBanner().getW());
73+
banner.put("h", bidImp.getBanner().getH());
74+
alvaAdsImp.setBanner(banner);
75+
alvaAdsImp.setTagid(bidImp.getTagid());
76+
alvaAdsImp.setBidfloor(bidImp.getBidfloor());
77+
this.imp = Collections.singletonList(alvaAdsImp);
78+
}
79+
request.setImp(this.imp);
80+
request.setDevice(this.device != null ? this.device : bidRequest.getDevice());
81+
request.setUser(this.user != null ? this.user : bidRequest.getUser());
82+
request.setRegs(this.regs != null ? this.regs : bidRequest.getRegs());
83+
if (this.site == null) {
84+
this.site = new AlvadsRequestORTB.AlvaAdsSite();
85+
site.setPage(bidRequest.getSite().getPage());
86+
site.setRef(bidRequest.getSite().getPage());
87+
final Map<String, Object> publisher = new HashMap<>();
88+
publisher.put("id", ext.getPublisherUniqueId());
89+
site.setPublisher(publisher);
90+
}
91+
request.setSite(this.site);
92+
return request;
93+
}
94+
95+
public AlvadsRequestORTB buildVideo() {
96+
final AlvadsRequestORTB request = new AlvadsRequestORTB();
97+
request.setId(this.id != null ? this.id : this.bidRequest.getId());
98+
if (this.imp == null) {
99+
final AlvadsRequestORTB.AlvaAdsImp alvaAdsImp = new AlvadsRequestORTB.AlvaAdsImp();
100+
alvaAdsImp.setId(this.bidImp.getId());
101+
final Map<String, Object> video = new HashMap<>();
102+
video.put("w", bidImp.getVideo().getW());
103+
video.put("h", bidImp.getVideo().getH());
104+
alvaAdsImp.setVideo(video);
105+
alvaAdsImp.setTagid(bidImp.getTagid());
106+
alvaAdsImp.setBidfloor(bidImp.getBidfloor());
107+
this.imp = Collections.singletonList(alvaAdsImp);
108+
}
109+
request.setImp(this.imp);
110+
request.setDevice(this.device != null ? this.device : bidRequest.getDevice());
111+
request.setUser(this.user != null ? this.user : bidRequest.getUser());
112+
request.setRegs(this.regs != null ? this.regs : bidRequest.getRegs());
113+
if (this.site == null) {
114+
this.site = new AlvadsRequestORTB.AlvaAdsSite();
115+
site.setPage(bidRequest.getSite().getPage());
116+
site.setRef(bidRequest.getSite().getPage());
117+
final Map<String, Object> publisher = new HashMap<>();
118+
publisher.put("id", ext.getPublisherUniqueId());
119+
site.setPublisher(publisher);
120+
}
121+
request.setSite(this.site);
122+
return request;
123+
}
124+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package org.prebid.server.bidder.alvads;
2+
3+
import com.iab.openrtb.request.BidRequest;
4+
import com.iab.openrtb.request.Imp;
5+
import com.iab.openrtb.response.Bid;
6+
import com.iab.openrtb.response.BidResponse;
7+
import com.iab.openrtb.response.SeatBid;
8+
import io.vertx.core.http.HttpMethod;
9+
import org.apache.commons.collections4.CollectionUtils;
10+
import org.prebid.server.bidder.Bidder;
11+
import org.prebid.server.bidder.mgid.model.ExtBidMgid;
12+
import org.prebid.server.bidder.model.BidderBid;
13+
import org.prebid.server.bidder.model.BidderCall;
14+
import org.prebid.server.bidder.model.BidderError;
15+
import org.prebid.server.bidder.model.HttpRequest;
16+
import org.prebid.server.bidder.model.Result;
17+
import org.prebid.server.exception.PreBidException;
18+
import org.prebid.server.json.JacksonMapper;
19+
import org.prebid.server.log.Logger;
20+
import org.prebid.server.log.LoggerFactory;
21+
import org.prebid.server.proto.openrtb.ext.response.BidType;
22+
import org.prebid.server.util.HttpUtil;
23+
24+
import java.util.ArrayList;
25+
import java.util.Collection;
26+
import java.util.Collections;
27+
import java.util.List;
28+
import java.util.Objects;
29+
30+
public class AlvadsBidder implements Bidder<AlvadsRequestORTB> {
31+
32+
private static final Logger logger = LoggerFactory.getLogger(AlvadsBidder.class);
33+
private final String endpointUrl;
34+
private final JacksonMapper mapper;
35+
36+
public AlvadsBidder(String endpointUrl, JacksonMapper mapper) {
37+
logger.debug("AlvaAdsBidder");
38+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
39+
this.mapper = Objects.requireNonNull(mapper);
40+
}
41+
42+
@Override
43+
public final Result<List<HttpRequest<AlvadsRequestORTB>>> makeHttpRequests(BidRequest bidRequest) {
44+
final List<BidderError> errors = new ArrayList<>();
45+
final List<HttpRequest<AlvadsRequestORTB>> httpRequests = new ArrayList<>();
46+
47+
for (Imp imp : bidRequest.getImp()) {
48+
try {
49+
final AlvadsImpExt impExt = parseImpExt(imp);
50+
final HttpRequest<AlvadsRequestORTB> request = makeHttpRequest(bidRequest, imp, impExt);
51+
52+
httpRequests.add(request);
53+
} catch (PreBidException e) {
54+
return Result.withError(BidderError.badInput(e.getMessage()));
55+
}
56+
}
57+
58+
if (httpRequests.isEmpty()) {
59+
return Result.withError(BidderError.badInput("found no valid impressions"));
60+
}
61+
62+
return Result.of(httpRequests, errors);
63+
}
64+
65+
private HttpRequest<AlvadsRequestORTB> makeHttpRequest(BidRequest request, Imp imp, AlvadsImpExt impExt) {
66+
final AlvaRequestORTBBuilder builder = new AlvaRequestORTBBuilder(request, imp, impExt, mapper);
67+
final AlvadsRequestORTB alvadsRequest = imp.getVideo() != null ? builder.buildVideo() : builder.build();
68+
return createRequest(alvadsRequest, impExt);
69+
}
70+
71+
private HttpRequest<AlvadsRequestORTB> createRequest(AlvadsRequestORTB request, AlvadsImpExt impExt) {
72+
return HttpRequest.<AlvadsRequestORTB>builder()
73+
.method(HttpMethod.POST)
74+
.uri(impExt.getEndPointUrl() != null ? impExt.getEndPointUrl() : endpointUrl)
75+
.headers(HttpUtil.headers())
76+
.payload(request)
77+
.body(mapper.encodeToBytes(request))
78+
.build();
79+
}
80+
81+
private AlvadsImpExt parseImpExt(Imp imp) {
82+
try {
83+
return mapper.mapper().convertValue(imp.getExt().get("bidder"), AlvadsImpExt.class);
84+
} catch (IllegalArgumentException e) {
85+
throw new PreBidException(e.getMessage(), e);
86+
}
87+
}
88+
89+
@Override
90+
public final Result<List<BidderBid>> makeBids(BidderCall<AlvadsRequestORTB> httpCall,
91+
BidRequest bidRequest) {
92+
try {
93+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
94+
return Result.withValues(extractBids(bidResponse, httpCall.getRequest().getPayload()));
95+
} catch (Exception e) {
96+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
97+
}
98+
}
99+
100+
private List<BidderBid> extractBids(BidResponse bidResponse, AlvadsRequestORTB request) {
101+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
102+
return Collections.emptyList();
103+
}
104+
return bidsFromResponse(bidResponse, request);
105+
}
106+
107+
private List<BidderBid> bidsFromResponse(BidResponse bidResponse, AlvadsRequestORTB request) {
108+
return bidResponse.getSeatbid().stream()
109+
.filter(Objects::nonNull)
110+
.map(SeatBid::getBid)
111+
.filter(Objects::nonNull)
112+
.flatMap(Collection::stream)
113+
.map(bid -> BidderBid.of(bid, getBidType(bid, request), bidResponse.getCur()))
114+
.toList();
115+
}
116+
117+
private BidType getBidType(Bid bid, AlvadsRequestORTB request) {
118+
final ExtBidMgid bidExt = getBidExt(bid);
119+
if (bidExt == null) {
120+
return BidType.banner;
121+
}
122+
123+
final BidType crtype = bidExt.getCrtype();
124+
return request.getImp().get(0).getVideo() != null ? BidType.video : crtype == null ? BidType.banner : crtype;
125+
}
126+
127+
private ExtBidMgid getBidExt(Bid bid) {
128+
try {
129+
return mapper.mapper().convertValue(bid.getExt(), ExtBidMgid.class);
130+
} catch (IllegalArgumentException e) {
131+
return null;
132+
}
133+
}
134+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.prebid.server.bidder.alvads;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import lombok.Value;
6+
7+
@JsonIgnoreProperties(ignoreUnknown = true)
8+
@Value(staticConstructor = "of")
9+
public class AlvadsImpExt {
10+
11+
@JsonProperty("publisherUniqueId")
12+
String publisherUniqueId;
13+
14+
@JsonProperty("endPointUrl")
15+
String endPointUrl;
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.prebid.server.bidder.alvads;
2+
3+
import lombok.Data;
4+
5+
@Data
6+
public class AlvadsPlacement {
7+
8+
private String divId;
9+
private String type;
10+
private String size;
11+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.prebid.server.bidder.alvads;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.iab.openrtb.request.Device;
5+
import com.iab.openrtb.request.Regs;
6+
import com.iab.openrtb.request.User;
7+
import lombok.Data;
8+
9+
import java.math.BigDecimal;
10+
import java.util.List;
11+
import java.util.Map;
12+
13+
@JsonInclude(JsonInclude.Include.NON_NULL)
14+
@Data
15+
public class AlvadsRequestORTB {
16+
17+
private String id;
18+
private List<AlvaAdsImp> imp;
19+
private Device device;
20+
private User user;
21+
private Regs regs;
22+
private AlvaAdsSite site;
23+
24+
@Data
25+
public static class AlvaAdsSite {
26+
private String page;
27+
private String ref;
28+
private Map<String, Object> publisher;
29+
30+
public AlvaAdsSite() {
31+
}
32+
}
33+
34+
@Data
35+
@JsonInclude(JsonInclude.Include.NON_NULL)
36+
public static class AlvaAdsImp {
37+
private String id;
38+
private Map<String, Object> banner;
39+
private Map<String, Object> video;
40+
private String tagid;
41+
private BigDecimal bidfloor;
42+
}
43+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.prebid.server.spring.config.bidder;
2+
3+
import org.prebid.server.bidder.BidderDeps;
4+
import org.prebid.server.bidder.alvads.AlvadsBidder;
5+
import org.prebid.server.json.JacksonMapper;
6+
import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
7+
import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
8+
import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
9+
import org.prebid.server.spring.env.YamlPropertySourceFactory;
10+
import org.springframework.beans.factory.annotation.Value;
11+
import org.springframework.boot.context.properties.ConfigurationProperties;
12+
import org.springframework.context.annotation.Bean;
13+
import org.springframework.context.annotation.Configuration;
14+
import org.springframework.context.annotation.PropertySource;
15+
16+
import jakarta.validation.constraints.NotBlank;
17+
18+
@Configuration
19+
@PropertySource(value = "classpath:/bidder-config/alvads.yaml", factory = YamlPropertySourceFactory.class)
20+
public class AlvaAdsConfiguration {
21+
22+
private static final String BIDDER_NAME = "alvads";
23+
24+
@Bean("alvaAdsConfigurationProperties")
25+
@ConfigurationProperties("adapters.alvads")
26+
BidderConfigurationProperties configurationProperties() {
27+
return new BidderConfigurationProperties();
28+
}
29+
30+
@Bean
31+
BidderDeps alvaAdsBidderDeps(BidderConfigurationProperties alvaAdsConfigurationProperties,
32+
@NotBlank @Value("${external-url}") String externalUrl,
33+
JacksonMapper mapper) {
34+
35+
return BidderDepsAssembler.forBidder(BIDDER_NAME)
36+
.withConfig(alvaAdsConfigurationProperties)
37+
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
38+
.bidderCreator(config -> new AlvadsBidder(config.getEndpoint(), mapper))
39+
.assemble();
40+
}
41+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
adapters:
2+
alvads:
3+
endpoint: https://helios-ads-qa-core.ssidevops.com/decision/openrtb
4+
meta-info:
5+
maintainer-email: alvads@oyealva.com
6+
app-media-types:
7+
- banner
8+
- video
9+
site-media-types:
10+
- banner
11+
- video

0 commit comments

Comments
 (0)