Skip to content

Commit 81c237c

Browse files
New Adapter: TeqBlaze (#4161)
1 parent 27d6364 commit 81c237c

23 files changed

+1177
-0
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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.StringUtils;
12+
import org.prebid.server.bidder.Bidder;
13+
import org.prebid.server.bidder.model.BidderBid;
14+
import org.prebid.server.bidder.model.BidderCall;
15+
import org.prebid.server.bidder.model.BidderError;
16+
import org.prebid.server.bidder.model.HttpRequest;
17+
import org.prebid.server.bidder.model.Result;
18+
import org.prebid.server.bidder.teqblaze.proto.TeqblazeExtImp;
19+
import org.prebid.server.exception.PreBidException;
20+
import org.prebid.server.json.DecodeException;
21+
import org.prebid.server.json.JacksonMapper;
22+
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
23+
import org.prebid.server.proto.openrtb.ext.request.teqblaze.ExtImpTeqblaze;
24+
import org.prebid.server.proto.openrtb.ext.response.BidType;
25+
import org.prebid.server.util.BidderUtil;
26+
import org.prebid.server.util.HttpUtil;
27+
28+
import java.util.ArrayList;
29+
import java.util.Collection;
30+
import java.util.Collections;
31+
import java.util.List;
32+
import java.util.Objects;
33+
34+
public class TeqblazeBidder implements Bidder<BidRequest> {
35+
36+
private static final TypeReference<ExtPrebid<?, ExtImpTeqblaze>> EXT_TYPE_REF = new TypeReference<>() {
37+
};
38+
39+
private static final String PUBLISHER_PROPERTY = "publisher";
40+
private static final String NETWORK_PROPERTY = "network";
41+
private static final String BIDDER_PROPERTY = "bidder";
42+
43+
private final String endpointUrl;
44+
private final JacksonMapper mapper;
45+
46+
public TeqblazeBidder(String endpointUrl, JacksonMapper mapper) {
47+
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
48+
this.mapper = Objects.requireNonNull(mapper);
49+
}
50+
51+
@Override
52+
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
53+
final List<BidderError> errors = new ArrayList<>();
54+
final List<HttpRequest<BidRequest>> httpRequests = new ArrayList<>();
55+
56+
for (Imp imp : request.getImp()) {
57+
try {
58+
final ExtImpTeqblaze extImpTeqblaze = parseExtImp(imp);
59+
final Imp modifiedImp = modifyImp(imp, extImpTeqblaze);
60+
httpRequests.add(makeHttpRequest(request, modifiedImp));
61+
} catch (PreBidException e) {
62+
errors.add(BidderError.badInput(e.getMessage()));
63+
}
64+
}
65+
66+
return httpRequests.isEmpty()
67+
? Result.withErrors(errors)
68+
: Result.of(httpRequests, errors);
69+
}
70+
71+
private ExtImpTeqblaze parseExtImp(Imp imp) {
72+
try {
73+
return mapper.mapper().convertValue(imp.getExt(), EXT_TYPE_REF).getBidder();
74+
} catch (IllegalArgumentException e) {
75+
throw new PreBidException("Cannot deserialize ExtImpTeqblaze: " + e.getMessage());
76+
}
77+
}
78+
79+
private Imp modifyImp(Imp imp, ExtImpTeqblaze extImpTeqblaze) {
80+
final TeqblazeExtImp impExtTeqblazeWithType = resolveImpExt(extImpTeqblaze);
81+
final ObjectNode modifiedImpExtBidder = mapper.mapper().createObjectNode();
82+
modifiedImpExtBidder.set(BIDDER_PROPERTY, mapper.mapper().valueToTree(impExtTeqblazeWithType));
83+
84+
return imp.toBuilder().ext(modifiedImpExtBidder).build();
85+
}
86+
87+
private TeqblazeExtImp resolveImpExt(ExtImpTeqblaze extImpTeqblaze) {
88+
final TeqblazeExtImp.TeqblazeExtImpBuilder builder = TeqblazeExtImp.builder();
89+
90+
if (StringUtils.isNotEmpty(extImpTeqblaze.getPlacementId())) {
91+
builder.type(PUBLISHER_PROPERTY).placementId(extImpTeqblaze.getPlacementId());
92+
} else if (StringUtils.isNotEmpty(extImpTeqblaze.getEndpointId())) {
93+
builder.type(NETWORK_PROPERTY).endpointId(extImpTeqblaze.getEndpointId());
94+
}
95+
96+
return builder.build();
97+
}
98+
99+
private HttpRequest<BidRequest> makeHttpRequest(BidRequest request, Imp imp) {
100+
final BidRequest outgoingRequest = request.toBuilder().imp(List.of(imp)).build();
101+
102+
return BidderUtil.defaultRequest(outgoingRequest, endpointUrl, mapper);
103+
}
104+
105+
@Override
106+
public Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
107+
try {
108+
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
109+
final List<BidderBid> bids = extractBids(bidResponse);
110+
return Result.withValues(bids);
111+
} catch (DecodeException | PreBidException e) {
112+
return Result.withError(BidderError.badServerResponse(e.getMessage()));
113+
}
114+
}
115+
116+
private List<BidderBid> extractBids(BidResponse bidResponse) {
117+
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
118+
return Collections.emptyList();
119+
}
120+
121+
return bidResponse.getSeatbid().stream()
122+
.filter(Objects::nonNull)
123+
.map(SeatBid::getBid)
124+
.filter(Objects::nonNull)
125+
.flatMap(Collection::stream)
126+
.filter(Objects::nonNull)
127+
.map(bid -> BidderBid.of(bid, getBidType(bid), bidResponse.getCur()))
128+
.toList();
129+
}
130+
131+
private BidType getBidType(Bid bid) {
132+
return switch (bid.getMtype()) {
133+
case 1 -> BidType.banner;
134+
case 2 -> BidType.video;
135+
case 3 -> BidType.audio;
136+
case 4 -> BidType.xNative;
137+
case null, default ->
138+
throw new PreBidException("could not define media type for impression: " + bid.getImpid());
139+
};
140+
}
141+
}
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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
adapters:
2+
teqblaze:
3+
endpoint: http://
4+
aliases:
5+
pinkLion:
6+
enabled: false
7+
endpoint: https://us-east-ep.pinklion.io/pserver
8+
meta-info:
9+
maintainer-email: [email protected]
10+
rocketlab:
11+
enabled: false
12+
endpoint: https://traffic1.rocketlab.ai/pserver
13+
meta-info:
14+
maintainer-email: [email protected]
15+
usersync:
16+
enabled: true
17+
cookie-family-name: rocketlab
18+
redirect:
19+
url: https://usync.rocketlab.ai/pbserver?gdpr={{gdpr}}&consent={{gdpr_consent}}&us_privacy={{us_privacy}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}&redirect={{redirect_url}}
20+
support-cors: false
21+
uid-macro: '[UID]'
22+
meta-info:
23+
maintainer-email: [email protected]
24+
app-media-types:
25+
- banner
26+
- video
27+
- native
28+
site-media-types:
29+
- banner
30+
- video
31+
- native
32+
supported-vendors:
33+
vendor-id: 0
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)