Skip to content
119 changes: 119 additions & 0 deletions src/main/java/org/prebid/server/bidder/kueezrtb/KueezRtbBidder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package org.prebid.server.bidder.kueezrtb;

import com.fasterxml.jackson.core.type.TypeReference;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.bidder.Bidder;
import org.prebid.server.bidder.model.BidderBid;
import org.prebid.server.bidder.model.BidderCall;
import org.prebid.server.bidder.model.BidderError;
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.Result;
import org.prebid.server.exception.PreBidException;
import org.prebid.server.json.DecodeException;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
import org.prebid.server.proto.openrtb.ext.request.kueezrtb.KueezRtbImpExt;
import org.prebid.server.proto.openrtb.ext.response.BidType;
import org.prebid.server.util.BidderUtil;
import org.prebid.server.util.HttpUtil;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

public class KueezRtbBidder implements Bidder<BidRequest> {

private static final TypeReference<ExtPrebid<?, KueezRtbImpExt>> TYPE_REFERENCE = new TypeReference<>() { };

private final String endpointUrl;
private final JacksonMapper mapper;

public KueezRtbBidder(String endpointUrl, JacksonMapper mapper) {
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
this.mapper = Objects.requireNonNull(mapper);
}

@Override
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest bidRequest) {
final List<HttpRequest<BidRequest>> requests = new ArrayList<>();
final List<BidderError> errors = new ArrayList<>();

for (Imp imp : bidRequest.getImp()) {
try {
final KueezRtbImpExt impExt = parseImpExt(imp);
requests.add(makeHttpRequest(bidRequest, imp, impExt));
} catch (PreBidException e) {
errors.add(BidderError.badInput(e.getMessage()));
}
}
return Result.of(requests, errors);
}

private KueezRtbImpExt parseImpExt(Imp imp) throws PreBidException {
try {
return mapper.mapper().convertValue(imp.getExt(), TYPE_REFERENCE).getBidder();
} catch (IllegalArgumentException e) {
throw new PreBidException(e.getMessage());
}
}

private HttpRequest<BidRequest> makeHttpRequest(BidRequest bidRequest, Imp imp, KueezRtbImpExt impExt) {
final BidRequest modifiedBidRequest = bidRequest.toBuilder().imp(Collections.singletonList(imp)).build();
final String uri = endpointUrl + HttpUtil.encodeUrl(StringUtils.defaultString(impExt.getConnectionId()).trim());

return BidderUtil.defaultRequest(modifiedBidRequest, uri, mapper);
}

@Override
public final Result<List<BidderBid>> makeBids(BidderCall<BidRequest> httpCall, BidRequest bidRequest) {
final List<BidderError> errors = new ArrayList<>();
try {
final BidResponse bidResponse = mapper.decodeValue(httpCall.getResponse().getBody(), BidResponse.class);
return Result.of(extractBids(bidResponse, errors), errors);
} catch (DecodeException | PreBidException e) {
return Result.withError(BidderError.badServerResponse(e.getMessage()));
}
}

private static List<BidderBid> extractBids(BidResponse bidResponse, List<BidderError> errors) {
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
return Collections.emptyList();
}
return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
.map(SeatBid::getBid)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.map(bid -> makeBid(bid, bidResponse.getCur(), errors))
.filter(Objects::nonNull)
.toList();
}

private static BidderBid makeBid(Bid bid, String currency, List<BidderError> errors) {
try {
final BidType mediaType = getMediaTypeForBid(bid);
return BidderBid.of(bid, mediaType, currency);
} catch (PreBidException e) {
errors.add(BidderError.badServerResponse(e.getMessage()));
return null;
}
}

private static BidType getMediaTypeForBid(Bid bid) {
final Integer mType = bid.getMtype();
return switch (mType) {
case 1 -> BidType.banner;
case 2 -> BidType.video;
case null, default -> throw new PreBidException("Could not define bid type for imp: " + bid.getImpid());
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.prebid.server.proto.openrtb.ext.request.kueezrtb;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Value;

@Value(staticConstructor = "of")
public class KueezRtbImpExt {

@JsonProperty("cId")
String connectionId;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.prebid.server.spring.config.bidder;

import org.prebid.server.bidder.BidderDeps;
import org.prebid.server.bidder.kueezrtb.KueezRtbBidder;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
import org.prebid.server.spring.env.YamlPropertySourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import jakarta.validation.constraints.NotBlank;

@Configuration
@PropertySource(value = "classpath:/bidder-config/kueezrtb.yaml", factory = YamlPropertySourceFactory.class)
public class KueezRtbConfiguration {

private static final String BIDDER_NAME = "kueezrtb";

@Bean("kueezrtbConfigurationProperties")
@ConfigurationProperties("adapters.kueezrtb")
BidderConfigurationProperties configurationProperties() {
return new BidderConfigurationProperties();
}

@Bean
BidderDeps kueezrtbBidderDeps(BidderConfigurationProperties kueezrtbConfigurationProperties,
@NotBlank @Value("${external-url}") String externalUrl,
JacksonMapper mapper) {

return BidderDepsAssembler.forBidder(BIDDER_NAME)
.withConfig(kueezrtbConfigurationProperties)
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
.bidderCreator(config -> new KueezRtbBidder(config.getEndpoint(), mapper))
.assemble();
}
}
20 changes: 20 additions & 0 deletions src/main/resources/bidder-config/kueezrtb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
adapters:
kueezrtb:
endpoint: https://prebidsrvr.kueezrtb.com/openrtb/
endpoint-compression: gzip
meta-info:
maintainer-email: rtb@kueez.com
app-media-types:
- banner
- video
site-media-types:
- banner
- video
supported-vendors:
vendor-id: 1165
usersync:
cookie-family-name: kueezrtb
iframe:
url: https://sync.kueezrtb.com/api/user/html/62ce79e7dd15099534ae5e04?pbs=true&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&redirect={{redirect_url}}&gpp={{gpp}}&gpp_sid={{gpp_sid}}
support-cors: false
uid-macro: '${userId}'
18 changes: 18 additions & 0 deletions src/main/resources/static/bidder-params/kueezrtb.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Kueez RTB Adapter Params",
"description": "A schema which validates params accepted by the Kueez RTB adapter",
"type": "object",
"properties": {
"cId": {
"type": "string",
"description": "The connection id.",
"minLength": 1,
"pattern": "^[a-zA-Z0-9_]+$"
}
},
"required": [
"cId"
],
"additionalProperties": false
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Golang version of this schema https://github.com/prebid/prebid-server/blob/0b91679fe610a50f898daaf3feb2a33440a33b26/static/bidder-params/kueezrtb.json#L17 restricts additional properties, where is the actual version of the schema?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kim-ng93 can you please confirm this or create a PR updating the schema?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should match the golang version now

Loading
Loading