Skip to content

Commit f946d26

Browse files
New Adapter: TeqBlaze
1 parent 8cf9c00 commit f946d26

13 files changed

Lines changed: 785 additions & 0 deletions

File tree

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package org.prebid.server.bidder.teqblaze;
2+
3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import com.fasterxml.jackson.databind.node.ObjectNode;
5+
import com.iab.openrtb.request.BidRequest;
6+
import com.iab.openrtb.request.Imp;
7+
import com.iab.openrtb.response.Bid;
8+
import com.iab.openrtb.response.BidResponse;
9+
import com.iab.openrtb.response.SeatBid;
10+
import org.apache.commons.collections4.CollectionUtils;
11+
import org.apache.commons.lang3.ObjectUtils;
12+
import org.apache.commons.lang3.StringUtils;
13+
import org.prebid.server.bidder.Bidder;
14+
import org.prebid.server.bidder.model.BidderBid;
15+
import org.prebid.server.bidder.model.BidderCall;
16+
import org.prebid.server.bidder.model.BidderError;
17+
import org.prebid.server.bidder.model.HttpRequest;
18+
import org.prebid.server.bidder.model.Result;
19+
import org.prebid.server.bidder.teqblaze.proto.TeqblazeExtImp;
20+
import org.prebid.server.exception.PreBidException;
21+
import org.prebid.server.json.DecodeException;
22+
import org.prebid.server.json.JacksonMapper;
23+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
24+
import org.prebid.server.proto.openrtb.ext.request.teqblaze.ExtImpTeqblaze;
25+
import org.prebid.server.proto.openrtb.ext.response.BidType;
26+
import org.prebid.server.util.BidderUtil;
27+
import org.prebid.server.util.HttpUtil;
28+
29+
import java.util.ArrayList;
30+
import java.util.Collection;
31+
import java.util.Collections;
32+
import java.util.List;
33+
import java.util.Objects;
34+
35+
public class TeqblazeBidder implements Bidder<BidRequest> {
36+
37+
private static final TypeReference<ExtPrebid<?, ExtImpTeqblaze>> EXT_TYPE_REF = new TypeReference<>() {
38+
};
39+
40+
private static final String PUBLISHER_PROPERTY = "publisher";
41+
private static final String NETWORK_PROPERTY = "network";
42+
private static final String BIDDER_PROPERTY = "bidder";
43+
44+
private final String endpointUrl;
45+
private final JacksonMapper mapper;
46+
47+
public TeqblazeBidder(String endpointUrl, JacksonMapper mapper) {
48+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
49+
this.mapper = Objects.requireNonNull(mapper);
50+
}
51+
52+
@Override
53+
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
54+
final List<BidderError> errors = new ArrayList<>();
55+
final List<HttpRequest<BidRequest>> httpRequests = new ArrayList<>();
56+
57+
for (Imp imp : request.getImp()) {
58+
try {
59+
final ExtImpTeqblaze extImpTeqblaze = parseExtImp(imp);
60+
final Imp modifiedImp = modifyImp(imp, extImpTeqblaze);
61+
httpRequests.add(makeHttpRequest(request, modifiedImp));
62+
} catch (PreBidException e) {
63+
errors.add(BidderError.badInput(e.getMessage()));
64+
}
65+
}
66+
67+
if (httpRequests.isEmpty()) {
68+
return Result.withError(BidderError.badInput("found no valid impressions"));
69+
}
70+
71+
return Result.of(httpRequests, errors);
72+
}
73+
74+
private ExtImpTeqblaze parseExtImp(Imp imp) {
75+
try {
76+
return mapper.mapper().convertValue(imp.getExt(), EXT_TYPE_REF).getBidder();
77+
} catch (IllegalArgumentException e) {
78+
throw new PreBidException("Cannot deserialize ExtImpLoyal: " + e.getMessage());
79+
}
80+
}
81+
82+
private Imp modifyImp(Imp imp, ExtImpTeqblaze extImpTeqblaze) {
83+
final TeqblazeExtImp impExtTeqblazeWithType = resolveImpExt(extImpTeqblaze);
84+
final ObjectNode modifiedImpExtBidder = mapper.mapper().createObjectNode();
85+
modifiedImpExtBidder.set(BIDDER_PROPERTY, mapper.mapper().valueToTree(impExtTeqblazeWithType));
86+
87+
return imp.toBuilder().ext(modifiedImpExtBidder).build();
88+
}
89+
90+
private TeqblazeExtImp resolveImpExt(ExtImpTeqblaze extImpTeqblaze) {
91+
final TeqblazeExtImp.TeqblazeExtImpBuilder builder = TeqblazeExtImp.builder();
92+
93+
if (StringUtils.isNotEmpty(extImpTeqblaze.getPlacementId())) {
94+
builder.type(PUBLISHER_PROPERTY).placementId(extImpTeqblaze.getPlacementId());
95+
} else if (StringUtils.isNotEmpty(extImpTeqblaze.getEndpointId())) {
96+
builder.type(NETWORK_PROPERTY).endpointId(extImpTeqblaze.getEndpointId());
97+
}
98+
99+
return builder.build();
100+
}
101+
102+
private HttpRequest<BidRequest> makeHttpRequest(BidRequest request, Imp imp) {
103+
final BidRequest outgoingRequest = request.toBuilder().imp(List.of(imp)).build();
104+
105+
return BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper);
106+
}
107+
108+
@Override
109+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
110+
try {
111+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
112+
final List<BidderBid> bids = extractBids(bidResponse);
113+
return Result.withValues(bids);
114+
} catch (DecodeException | PreBidException e) {
115+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
116+
}
117+
}
118+
119+
private List<BidderBid> extractBids(BidResponse bidResponse) {
120+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
121+
return Collections.emptyList();
122+
}
123+
124+
return bidResponse.getSeatbid().stream()
125+
.filter(Objects::nonNull)
126+
.map(SeatBid::getBid)
127+
.filter(Objects::nonNull)
128+
.flatMap(Collection::stream)
129+
.filter(Objects::nonNull)
130+
.map(bid -> BidderBid.of(bid, getBidType(bid), bidResponse.getCur()))
131+
.toList();
132+
}
133+
134+
private BidType getBidType(Bid bid) {
135+
final Integer markupType = ObjectUtils.defaultIfNull(bid.getMtype(), 0);
136+
137+
return switch (markupType) {
138+
case 1 -> BidType.banner;
139+
case 2 -> BidType.video;
140+
case 3 -> BidType.audio;
141+
case 4 -> BidType.xNative;
142+
default -> throw new PreBidException(
143+
"could not define media type for impression: " + bid.getImpid());
144+
};
145+
}
146+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.prebid.server.bidder.teqblaze.proto;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Builder;
5+
import lombok.Value;
6+
7+
@Value
8+
@Builder
9+
public class TeqblazeExtImp {
10+
11+
String type;
12+
13+
@JsonProperty("placementId")
14+
String placementId;
15+
16+
@JsonProperty("endpointId")
17+
String endpointId;
18+
}
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.teqblaze;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Value;
5+
6+
@Value(staticConstructor = "of")
7+
public class ExtImpTeqblaze {
8+
9+
@JsonProperty("placementId")
10+
String placementId;
11+
12+
@JsonProperty("endpointId")
13+
String endpointId;
14+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.prebid.server.spring.config.bidder.util;
2+
3+
import org.prebid.server.bidder.BidderDeps;
4+
import org.prebid.server.bidder.teqblaze.TeqblazeBidder;
5+
import org.prebid.server.json.JacksonMapper;
6+
import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
7+
import org.prebid.server.spring.env.YamlPropertySourceFactory;
8+
import org.springframework.beans.factory.annotation.Value;
9+
import org.springframework.boot.context.properties.ConfigurationProperties;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.context.annotation.PropertySource;
13+
14+
import jakarta.validation.constraints.NotBlank;
15+
16+
@Configuration
17+
@PropertySource(value = "classpath:/bidder-config/teqblaze.yaml", factory = YamlPropertySourceFactory.class)
18+
public class TeqblazeConfiguration {
19+
20+
private static final String BIDDER_NAME = "teqblaze";
21+
22+
@Bean("teqblazeConfigurationProperties")
23+
@ConfigurationProperties("adapters.teqblaze")
24+
BidderConfigurationProperties configurationProperties() {
25+
return new BidderConfigurationProperties();
26+
}
27+
28+
@Bean
29+
BidderDeps teqblazeBidderDeps(BidderConfigurationProperties teqblazeConfigurationProperties,
30+
@NotBlank @Value("${external-url}") String externalUrl,
31+
JacksonMapper mapper) {
32+
33+
return BidderDepsAssembler.forBidder(BIDDER_NAME)
34+
.withConfig(teqblazeConfigurationProperties)
35+
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
36+
.bidderCreator(config -> new TeqblazeBidder(config.getEndpoint(), mapper))
37+
.assemble();
38+
}
39+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
adapters:
2+
teqblaze:
3+
meta-info:
4+
whiteLabelOnly: true
5+
maintainer-email: github@teqblaze.com
6+
app-media-types:
7+
- banner
8+
- video
9+
- native
10+
site-media-types:
11+
- banner
12+
- video
13+
- native
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": "Teqblaze Adapter Params",
4+
"description": "A schema which validates params accepted by the TeqBlaze 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)