Skip to content

Commit 98b7a77

Browse files
New Rediads Adapter (#4082)
1 parent a5bb6bb commit 98b7a77

File tree

12 files changed

+721
-0
lines changed

12 files changed

+721
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package org.prebid.server.bidder.rediads;
2+
3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import com.fasterxml.jackson.databind.node.ObjectNode;
5+
import com.iab.openrtb.request.App;
6+
import com.iab.openrtb.request.BidRequest;
7+
import com.iab.openrtb.request.Imp;
8+
import com.iab.openrtb.request.Publisher;
9+
import com.iab.openrtb.request.Site;
10+
import com.iab.openrtb.response.Bid;
11+
import com.iab.openrtb.response.BidResponse;
12+
import com.iab.openrtb.response.SeatBid;
13+
import org.apache.commons.collections4.CollectionUtils;
14+
import org.apache.commons.lang3.StringUtils;
15+
import org.prebid.server.bidder.Bidder;
16+
import org.prebid.server.bidder.model.BidderBid;
17+
import org.prebid.server.bidder.model.BidderCall;
18+
import org.prebid.server.bidder.model.BidderError;
19+
import org.prebid.server.bidder.model.HttpRequest;
20+
import org.prebid.server.bidder.model.Result;
21+
import org.prebid.server.exception.PreBidException;
22+
import org.prebid.server.json.DecodeException;
23+
import org.prebid.server.json.JacksonMapper;
24+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
25+
import org.prebid.server.proto.openrtb.ext.request.rediads.ExtImpRediads;
26+
import org.prebid.server.proto.openrtb.ext.response.BidType;
27+
import org.prebid.server.util.BidderUtil;
28+
import org.prebid.server.util.HttpUtil;
29+
30+
import java.util.ArrayList;
31+
import java.util.Collection;
32+
import java.util.Collections;
33+
import java.util.List;
34+
import java.util.Objects;
35+
import java.util.stream.Collectors;
36+
37+
public class RediadsBidder implements Bidder<BidRequest> {
38+
39+
private static final TypeReference<ExtPrebid<?, ExtImpRediads>> TYPE_REFERENCE = new TypeReference<>() {
40+
};
41+
private static final String SUBDOMAIN_MACRO = "{{SUBDOMAIN}}";
42+
43+
private final String endpointUrl;
44+
private final String defaultSubdomain;
45+
private final JacksonMapper mapper;
46+
47+
public RediadsBidder(String endpointUrl, JacksonMapper mapper, String defaultSubdomain) {
48+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
49+
this.mapper = Objects.requireNonNull(mapper);
50+
this.defaultSubdomain = Objects.requireNonNull(defaultSubdomain);
51+
}
52+
53+
@Override
54+
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
55+
final List<Imp> modifiedImps = new ArrayList<>();
56+
final List<BidderError> errors = new ArrayList<>();
57+
58+
String accountId = null;
59+
String endpoint = null;
60+
61+
for (Imp imp : request.getImp()) {
62+
try {
63+
final ExtImpRediads extImp = parseImpExt(imp);
64+
modifiedImps.add(modifyImp(imp, extImp));
65+
accountId = extImp.getAccountId();
66+
endpoint = extImp.getEndpoint();
67+
} catch (PreBidException e) {
68+
errors.add(BidderError.badInput(e.getMessage()));
69+
}
70+
}
71+
72+
if (modifiedImps.isEmpty()) {
73+
return Result.withErrors(errors);
74+
}
75+
76+
final BidRequest outgoingRequest = modifyRequest(request, modifiedImps, accountId);
77+
final String endpointUrl = resolveEndpointUrl(endpoint);
78+
final HttpRequest<BidRequest> httpRequest = BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper);
79+
80+
return Result.of(Collections.singletonList(httpRequest), errors);
81+
}
82+
83+
private ExtImpRediads parseImpExt(Imp imp) {
84+
try {
85+
return mapper.mapper().convertValue(imp.getExt(), TYPE_REFERENCE).getBidder();
86+
} catch (IllegalArgumentException e) {
87+
throw new PreBidException("Invalid imp.ext for impression " + imp.getId());
88+
}
89+
}
90+
91+
private Imp modifyImp(Imp imp, ExtImpRediads extImp) {
92+
final ObjectNode modifiedExt = imp.getExt().deepCopy();
93+
modifiedExt.remove("bidder");
94+
modifiedExt.remove("prebid");
95+
return imp.toBuilder()
96+
.tagid(StringUtils.defaultIfBlank(extImp.getSlot(), imp.getTagid()))
97+
.ext(modifiedExt)
98+
.build();
99+
}
100+
101+
private BidRequest modifyRequest(BidRequest request, List<Imp> imps, String accountId) {
102+
final Site site = request.getSite();
103+
final App app = request.getApp();
104+
return request.toBuilder()
105+
.site(site != null ? modifySite(site, accountId) : null)
106+
.app(site == null && app != null ? modifyApp(app, accountId) : app)
107+
.imp(imps)
108+
.build();
109+
}
110+
111+
private static Site modifySite(Site site, String accountId) {
112+
final Publisher originalPublisher = site.getPublisher();
113+
final Publisher newPublisher = originalPublisher != null
114+
? originalPublisher.toBuilder().id(accountId).build()
115+
: Publisher.builder().id(accountId).build();
116+
return site.toBuilder().publisher(newPublisher).build();
117+
}
118+
119+
private static App modifyApp(App app, String accountId) {
120+
final Publisher originalPublisher = app.getPublisher();
121+
final Publisher newPublisher = originalPublisher != null
122+
? originalPublisher.toBuilder().id(accountId).build()
123+
: Publisher.builder().id(accountId).build();
124+
return app.toBuilder().publisher(newPublisher).build();
125+
}
126+
127+
private String resolveEndpointUrl(String subdomain) {
128+
return endpointUrl.replace(SUBDOMAIN_MACRO, StringUtils.defaultIfBlank(subdomain, defaultSubdomain));
129+
}
130+
131+
@Override
132+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
133+
try {
134+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
135+
return Result.withValues(extractBids(bidResponse));
136+
} catch (DecodeException | PreBidException e) {
137+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
138+
}
139+
}
140+
141+
private static List<BidderBid> extractBids(BidResponse bidResponse) {
142+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
143+
return Collections.emptyList();
144+
}
145+
return bidsFromResponse(bidResponse);
146+
}
147+
148+
private static List<BidderBid> bidsFromResponse(BidResponse bidResponse) {
149+
return bidResponse.getSeatbid().stream()
150+
.filter(Objects::nonNull)
151+
.map(SeatBid::getBid)
152+
.filter(Objects::nonNull)
153+
.flatMap(Collection::stream)
154+
.filter(Objects::nonNull)
155+
.map(bid -> BidderBid.of(bid, getBidType(bid), bidResponse.getCur()))
156+
.collect(Collectors.toList());
157+
}
158+
159+
private static BidType getBidType(Bid bid) {
160+
return switch (bid.getMtype()) {
161+
case 1 -> BidType.banner;
162+
case 2 -> BidType.video;
163+
case 3 -> BidType.audio;
164+
case 4 -> BidType.xNative;
165+
case null, default -> throw new PreBidException(
166+
"could not define media type for impression: " + bid.getImpid());
167+
};
168+
}
169+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.prebid.server.proto.openrtb.ext.request.rediads;
2+
3+
import lombok.Value;
4+
5+
@Value(staticConstructor = "of")
6+
public class ExtImpRediads {
7+
8+
String accountId;
9+
10+
String slot;
11+
12+
String endpoint;
13+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package org.prebid.server.spring.config.bidder;
2+
3+
import lombok.Data;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.NoArgsConstructor;
6+
import org.prebid.server.bidder.BidderDeps;
7+
import org.prebid.server.bidder.rediads.RediadsBidder;
8+
import org.prebid.server.json.JacksonMapper;
9+
import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
10+
import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
11+
import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
12+
import org.prebid.server.spring.env.YamlPropertySourceFactory;
13+
import org.springframework.beans.factory.annotation.Value;
14+
import org.springframework.boot.context.properties.ConfigurationProperties;
15+
import org.springframework.context.annotation.Bean;
16+
import org.springframework.context.annotation.Configuration;
17+
import org.springframework.context.annotation.PropertySource;
18+
19+
import jakarta.validation.constraints.NotBlank;
20+
21+
@Configuration
22+
@PropertySource(value = "classpath:/bidder-config/rediads.yaml", factory = YamlPropertySourceFactory.class)
23+
public class RediadsConfiguration {
24+
25+
private static final String BIDDER_NAME = "rediads";
26+
27+
@Bean("rediadsConfigurationProperties")
28+
@ConfigurationProperties("adapters.rediads")
29+
RediadsConfigurationProperties configurationProperties() {
30+
return new RediadsConfigurationProperties();
31+
}
32+
33+
@Bean
34+
BidderDeps rediadsBidderDeps(RediadsConfigurationProperties rediadsConfigurationProperties,
35+
@NotBlank @Value("${external-url}") String externalUrl,
36+
JacksonMapper mapper) {
37+
38+
return BidderDepsAssembler.<RediadsConfigurationProperties>forBidder(BIDDER_NAME)
39+
.withConfig(rediadsConfigurationProperties)
40+
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
41+
.bidderCreator(config -> new RediadsBidder(
42+
config.getEndpoint(),
43+
mapper,
44+
config.getDefaultSubdomain()))
45+
.assemble();
46+
}
47+
48+
@Data
49+
@EqualsAndHashCode(callSuper = true)
50+
@NoArgsConstructor
51+
private static class RediadsConfigurationProperties extends BidderConfigurationProperties {
52+
53+
@NotBlank
54+
private String defaultSubdomain;
55+
}
56+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
adapters:
2+
rediads:
3+
endpoint: https://{{SUBDOMAIN}}.rediads.com/openrtb2/auction
4+
default-subdomain: bidding
5+
ortb-version: "2.6"
6+
modifying-vast-xml-allowed: true
7+
meta-info:
8+
maintainer-email: [email protected]
9+
app-media-types:
10+
- banner
11+
- video
12+
- audio
13+
- native
14+
site-media-types:
15+
- banner
16+
- video
17+
- audio
18+
- native
19+
supported-vendors:
20+
vendor-id: 0
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "RediAds Adapter Params",
4+
"description": "A schema which validates params accepted by the Rediads adapter",
5+
"type": "object",
6+
"properties": {
7+
"account_id": {
8+
"type": "string"
9+
},
10+
"slot": {
11+
"type": "string"
12+
},
13+
"endpoint": {
14+
"type": "string"
15+
}
16+
},
17+
"required": [
18+
"account_id"
19+
]
20+
}

0 commit comments

Comments
 (0)