Skip to content

Commit ad21014

Browse files
New Adapter: Alliance Gravity (#4502)
1 parent c86aa47 commit ad21014

12 files changed

Lines changed: 862 additions & 0 deletions

File tree

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package org.prebid.server.bidder.alliancegravity;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.core.type.TypeReference;
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
import com.iab.openrtb.request.BidRequest;
7+
import com.iab.openrtb.request.Device;
8+
import com.iab.openrtb.request.Imp;
9+
import com.iab.openrtb.request.Site;
10+
import com.iab.openrtb.request.User;
11+
import com.iab.openrtb.response.Bid;
12+
import com.iab.openrtb.response.BidResponse;
13+
import com.iab.openrtb.response.SeatBid;
14+
import io.vertx.core.MultiMap;
15+
import org.apache.commons.collections4.CollectionUtils;
16+
import org.apache.commons.lang3.StringUtils;
17+
import org.prebid.server.bidder.Bidder;
18+
import org.prebid.server.bidder.model.BidderBid;
19+
import org.prebid.server.bidder.model.BidderCall;
20+
import org.prebid.server.bidder.model.BidderError;
21+
import org.prebid.server.bidder.model.HttpRequest;
22+
import org.prebid.server.bidder.model.Result;
23+
import org.prebid.server.exception.PreBidException;
24+
import org.prebid.server.json.DecodeException;
25+
import org.prebid.server.json.JacksonMapper;
26+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
27+
import org.prebid.server.proto.openrtb.ext.request.ExtImpPrebid;
28+
import org.prebid.server.proto.openrtb.ext.request.ExtStoredRequest;
29+
import org.prebid.server.proto.openrtb.ext.request.alliancegravity.ExtImpAllianceGravity;
30+
import org.prebid.server.proto.openrtb.ext.response.BidType;
31+
import org.prebid.server.proto.openrtb.ext.response.ExtBidPrebid;
32+
import org.prebid.server.util.BidderUtil;
33+
import org.prebid.server.util.HttpUtil;
34+
35+
import java.util.ArrayList;
36+
import java.util.Collection;
37+
import java.util.Collections;
38+
import java.util.List;
39+
import java.util.Objects;
40+
import java.util.Optional;
41+
42+
public class AllianceGravityBidder implements Bidder<BidRequest> {
43+
44+
private static final TypeReference<ExtPrebid<?, ExtImpAllianceGravity>> TYPE_REFERENCE =
45+
new TypeReference<>() {
46+
};
47+
48+
private final String endpointUrl;
49+
private final JacksonMapper mapper;
50+
51+
public AllianceGravityBidder(String endpointUrl, JacksonMapper mapper) {
52+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
53+
this.mapper = Objects.requireNonNull(mapper);
54+
}
55+
56+
@Override
57+
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
58+
final List<Imp> modifiedImps = new ArrayList<>();
59+
final List<BidderError> errors = new ArrayList<>();
60+
61+
for (Imp imp : request.getImp()) {
62+
try {
63+
final ExtImpAllianceGravity extImp = parseImpExt(imp);
64+
modifiedImps.add(modifyImp(imp, extImp));
65+
} catch (PreBidException e) {
66+
errors.add(BidderError.badInput(e.getMessage()));
67+
}
68+
}
69+
70+
if (modifiedImps.isEmpty()) {
71+
return Result.withErrors(errors);
72+
}
73+
74+
final BidRequest modifiedRequest = request.toBuilder().imp(modifiedImps).build();
75+
return Result.of(
76+
Collections.singletonList(
77+
BidderUtil.defaultRequest(modifiedRequest, makeHeaders(modifiedRequest), endpointUrl, mapper)),
78+
errors);
79+
}
80+
81+
private ExtImpAllianceGravity parseImpExt(Imp imp) {
82+
try {
83+
return mapper.mapper().convertValue(imp.getExt(), TYPE_REFERENCE).getBidder();
84+
} catch (IllegalArgumentException e) {
85+
throw new PreBidException(e.getMessage());
86+
}
87+
}
88+
89+
private Imp modifyImp(Imp imp, ExtImpAllianceGravity extImp) {
90+
final ExtImpPrebid extImpPrebid = ExtImpPrebid.builder()
91+
.storedrequest(ExtStoredRequest.of(extImp.getSrId()))
92+
.build();
93+
94+
return imp.toBuilder()
95+
.ext(mapper.mapper().valueToTree(ExtPrebid.of(extImpPrebid, null)))
96+
.build();
97+
}
98+
99+
private static MultiMap makeHeaders(BidRequest request) {
100+
final MultiMap headers = HttpUtil.headers();
101+
102+
final Device device = request.getDevice();
103+
if (device != null) {
104+
HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.USER_AGENT_HEADER, device.getUa());
105+
final String forwardedFor = StringUtils.isNotBlank(device.getIp()) ? device.getIp() : device.getIpv6();
106+
HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.X_FORWARDED_FOR_HEADER, forwardedFor);
107+
}
108+
109+
final Site site = request.getSite();
110+
if (site != null) {
111+
HttpUtil.addHeaderIfValueIsNotEmpty(headers, HttpUtil.REFERER_HEADER, site.getPage());
112+
}
113+
114+
final User user = request.getUser();
115+
if (user != null && StringUtils.isNotBlank(user.getBuyeruid())) {
116+
headers.add(HttpUtil.COOKIE_HEADER, "uids=" + user.getBuyeruid());
117+
}
118+
119+
return headers;
120+
}
121+
122+
@Override
123+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
124+
try {
125+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
126+
final List<BidderError> errors = new ArrayList<>();
127+
return Result.of(extractBids(bidResponse, errors), errors);
128+
} catch (DecodeException e) {
129+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
130+
}
131+
}
132+
133+
private List<BidderBid> extractBids(BidResponse bidResponse, List<BidderError> errors) {
134+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
135+
return Collections.emptyList();
136+
}
137+
138+
return bidResponse.getSeatbid().stream()
139+
.filter(Objects::nonNull)
140+
.map(SeatBid::getBid)
141+
.filter(Objects::nonNull)
142+
.flatMap(Collection::stream)
143+
.filter(Objects::nonNull)
144+
.map(bid -> resolveBidderBid(bid, bidResponse.getCur(), errors))
145+
.filter(Objects::nonNull)
146+
.toList();
147+
}
148+
149+
private BidderBid resolveBidderBid(Bid bid, String currency, List<BidderError> errors) {
150+
try {
151+
return BidderBid.of(bid, getBidType(bid), currency);
152+
} catch (PreBidException e) {
153+
errors.add(BidderError.badServerResponse(e.getMessage()));
154+
return null;
155+
}
156+
}
157+
158+
private BidType getBidType(Bid bid) {
159+
return Optional.ofNullable(bid.getExt())
160+
.map(ext -> ext.get("prebid"))
161+
.filter(JsonNode::isObject)
162+
.map(this::parseExtBidPrebid)
163+
.map(ExtBidPrebid::getType)
164+
.orElseThrow(() -> new PreBidException(
165+
"Failed to parse impression \"%s\" mediatype".formatted(bid.getImpid())));
166+
}
167+
168+
private ExtBidPrebid parseExtBidPrebid(JsonNode prebidNode) {
169+
try {
170+
return mapper.mapper().treeToValue(prebidNode, ExtBidPrebid.class);
171+
} catch (JsonProcessingException e) {
172+
return null;
173+
}
174+
}
175+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.prebid.server.proto.openrtb.ext.request.alliancegravity;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Value;
5+
6+
@Value(staticConstructor = "of")
7+
public class ExtImpAllianceGravity {
8+
9+
@JsonProperty("srid")
10+
String srId;
11+
}
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.alliancegravity.AllianceGravityBidder;
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/alliancegravity.yaml", factory = YamlPropertySourceFactory.class)
20+
public class AllianceGravityConfiguration {
21+
22+
private static final String BIDDER_NAME = "alliance_gravity";
23+
24+
@Bean("alliancegravityConfigurationProperties")
25+
@ConfigurationProperties("adapters.alliancegravity")
26+
BidderConfigurationProperties configurationProperties() {
27+
return new BidderConfigurationProperties();
28+
}
29+
30+
@Bean
31+
BidderDeps alliancegravityBidderDeps(BidderConfigurationProperties alliancegravityConfigurationProperties,
32+
@NotBlank @Value("${external-url}") String externalUrl,
33+
JacksonMapper mapper) {
34+
35+
return BidderDepsAssembler.forBidder(BIDDER_NAME)
36+
.withConfig(alliancegravityConfigurationProperties)
37+
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
38+
.bidderCreator(config -> new AllianceGravityBidder(config.getEndpoint(), mapper))
39+
.assemble();
40+
}
41+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
adapters:
2+
alliancegravity:
3+
endpoint: https://pbs.production.agrvt.com/openrtb2/auction
4+
ortb-version: "2.6"
5+
modifying-vast-xml-allowed: true
6+
meta-info:
7+
maintainer-email: produit@alliancegravity.com
8+
app-media-types:
9+
- banner
10+
- video
11+
- audio
12+
- native
13+
site-media-types:
14+
- banner
15+
- video
16+
- audio
17+
- native
18+
supported-vendors:
19+
vendor-id: 501
20+
usersync:
21+
cookie-family-name: alliancegravity
22+
iframe:
23+
url: https://pbs.production.agrvt.com/static/cookie_sync.html?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect={{redirect_url}}
24+
support-cors: false
25+
uid-macro: '$UID'
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "Alliance Gravity Adapter Params",
4+
"description": "A schema which validates params accepted by the Alliance Gravity adapter",
5+
"type": "object",
6+
"properties": {
7+
"srid": {
8+
"type": "string",
9+
"description": "Stored request ID",
10+
"minLength": 1
11+
}
12+
},
13+
"required": ["srid"]
14+
}

0 commit comments

Comments
 (0)