Skip to content

Commit 4e8277d

Browse files
AntoxaAntoxicRitesh Ghodrao
authored andcommitted
New Smoot Adapter (prebid#4021)
1 parent 2352f1b commit 4e8277d

13 files changed

Lines changed: 713 additions & 0 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package org.prebid.server.bidder.smoot;
2+
3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import com.iab.openrtb.request.BidRequest;
5+
import com.iab.openrtb.request.Imp;
6+
import com.iab.openrtb.response.Bid;
7+
import com.iab.openrtb.response.BidResponse;
8+
import com.iab.openrtb.response.SeatBid;
9+
import org.apache.commons.collections4.CollectionUtils;
10+
import org.apache.commons.lang3.StringUtils;
11+
import org.prebid.server.bidder.Bidder;
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.DecodeException;
19+
import org.prebid.server.json.JacksonMapper;
20+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
21+
import org.prebid.server.proto.openrtb.ext.request.smoot.ExtImpSmoot;
22+
import org.prebid.server.proto.openrtb.ext.response.BidType;
23+
import org.prebid.server.util.BidderUtil;
24+
import org.prebid.server.util.HttpUtil;
25+
26+
import java.util.ArrayList;
27+
import java.util.Collection;
28+
import java.util.Collections;
29+
import java.util.List;
30+
import java.util.Objects;
31+
32+
public class SmootBidder implements Bidder<BidRequest> {
33+
34+
private static final TypeReference<ExtPrebid<?, ExtImpSmoot>> SMOOT_EXT_TYPE_REFERENCE = new TypeReference<>() {
35+
};
36+
37+
private final String endpointUrl;
38+
private final JacksonMapper mapper;
39+
40+
public SmootBidder(String endpointUrl, JacksonMapper mapper) {
41+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
42+
this.mapper = Objects.requireNonNull(mapper);
43+
}
44+
45+
@Override
46+
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
47+
final List<HttpRequest<BidRequest>> httpRequests = new ArrayList<>();
48+
final List<BidderError> errors = new ArrayList<>();
49+
50+
for (Imp imp : request.getImp()) {
51+
try {
52+
final ExtImpSmoot extImpSmoot = parseImpExt(imp);
53+
final Imp modifiedImp = modifyImp(imp, extImpSmoot);
54+
final BidRequest outgoingRequest = request.toBuilder()
55+
.imp(Collections.singletonList(modifiedImp))
56+
.build();
57+
httpRequests.add(BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper));
58+
} catch (PreBidException e) {
59+
errors.add(BidderError.badInput(e.getMessage()));
60+
}
61+
}
62+
63+
return Result.of(httpRequests, errors);
64+
}
65+
66+
private ExtImpSmoot parseImpExt(Imp imp) {
67+
try {
68+
return mapper.mapper().convertValue(imp.getExt(), SMOOT_EXT_TYPE_REFERENCE).getBidder();
69+
} catch (IllegalArgumentException e) {
70+
throw new PreBidException("Error parsing imp.ext: " + e.getMessage());
71+
}
72+
}
73+
74+
private Imp modifyImp(Imp imp, ExtImpSmoot extImpSmoot) {
75+
final SmootImpExt smootImpExt = StringUtils.isNotEmpty(extImpSmoot.getPlacementId())
76+
? SmootImpExt.publisher(extImpSmoot.getPlacementId())
77+
: SmootImpExt.network(extImpSmoot.getEndpointId());
78+
79+
return imp.toBuilder()
80+
.ext(mapper.mapper().createObjectNode().set("bidder", mapper.mapper().valueToTree(smootImpExt)))
81+
.build();
82+
}
83+
84+
@Override
85+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
86+
try {
87+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
88+
return Result.withValues(extractBids(bidResponse));
89+
} catch (DecodeException | PreBidException e) {
90+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
91+
}
92+
}
93+
94+
private static List<BidderBid> extractBids(BidResponse bidResponse) {
95+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
96+
return Collections.emptyList();
97+
}
98+
return bidResponse.getSeatbid().stream()
99+
.filter(Objects::nonNull)
100+
.map(SeatBid::getBid)
101+
.filter(Objects::nonNull)
102+
.flatMap(Collection::stream)
103+
.filter(Objects::nonNull)
104+
.map(bid -> BidderBid.of(bid, getBidType(bid), bidResponse.getCur()))
105+
.toList();
106+
}
107+
108+
private static BidType getBidType(Bid bid) {
109+
return switch (bid.getMtype()) {
110+
case 1 -> BidType.banner;
111+
case 2 -> BidType.video;
112+
case 4 -> BidType.xNative;
113+
case null, default ->
114+
throw new PreBidException("could not define media type for impression: " + bid.getImpid());
115+
};
116+
}
117+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.prebid.server.bidder.smoot;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Value;
5+
6+
@Value(staticConstructor = "of")
7+
public class SmootImpExt {
8+
9+
String type;
10+
11+
@JsonProperty("placementId")
12+
String placementId;
13+
14+
@JsonProperty("endpointId")
15+
String endpointId;
16+
17+
public static SmootImpExt publisher(String placementId) {
18+
return SmootImpExt.of("publisher", placementId, null);
19+
}
20+
21+
public static SmootImpExt network(String endpointId) {
22+
return SmootImpExt.of("network", null, endpointId);
23+
}
24+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.prebid.server.proto.openrtb.ext.request.smoot;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Value;
5+
6+
@Value(staticConstructor = "of")
7+
public class ExtImpSmoot {
8+
9+
@JsonProperty("placementId")
10+
String placementId;
11+
12+
@JsonProperty("endpointId")
13+
String endpointId;
14+
}
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.smoot.SmootBidder;
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/smoot.yaml", factory = YamlPropertySourceFactory.class)
20+
public class SmootConfiguration {
21+
22+
private static final String BIDDER_NAME = "smoot";
23+
24+
@Bean("smootConfigurationProperties")
25+
@ConfigurationProperties("adapters.smoot")
26+
BidderConfigurationProperties configurationProperties() {
27+
return new BidderConfigurationProperties();
28+
}
29+
30+
@Bean
31+
BidderDeps smootBidderDeps(BidderConfigurationProperties smootConfigurationProperties,
32+
@NotBlank @Value("${external-url}") String externalUrl,
33+
JacksonMapper mapper) {
34+
35+
return BidderDepsAssembler.forBidder(BIDDER_NAME)
36+
.withConfig(smootConfigurationProperties)
37+
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
38+
.bidderCreator(config -> new SmootBidder(config.getEndpoint(), mapper))
39+
.assemble();
40+
}
41+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
adapters:
2+
smoot:
3+
endpoint: 'https://endpoint1.smoot.ai/pserver'
4+
meta-info:
5+
maintainer-email: 'info@smoot.ai'
6+
app-media-types:
7+
- banner
8+
- video
9+
- native
10+
site-media-types:
11+
- banner
12+
- video
13+
- native
14+
supported-vendors:
15+
vendor-id: 0
16+
usersync:
17+
cookie-family-name: smoot
18+
redirect:
19+
support-cors: false
20+
url: 'https://usync.smxconv.com/pbserver?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redir={{redirect_url}}'
21+
uid-macro: '[UID]'
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "Smoot Adapter Params",
4+
"description": "A schema which validates params accepted by the Smoot adapter",
5+
"type": "object",
6+
"properties": {
7+
"placementId": {
8+
"type": "string",
9+
"minLength": 1,
10+
"description": "Placement ID"
11+
},
12+
"endpointId": {
13+
"type": "string",
14+
"minLength": 1,
15+
"description": "Endpoint ID"
16+
}
17+
},
18+
"oneOf": [
19+
{
20+
"required": [
21+
"placementId"
22+
]
23+
},
24+
{
25+
"required": [
26+
"endpointId"
27+
]
28+
}
29+
]
30+
}

0 commit comments

Comments
 (0)