Skip to content

Commit

Permalink
Add Logicad bidder (#887)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndriyPavlyuk committed Oct 5, 2020
1 parent ddaf5e9 commit fa3733a
Show file tree
Hide file tree
Showing 14 changed files with 819 additions and 0 deletions.
156 changes: 156 additions & 0 deletions src/main/java/org/prebid/server/bidder/logicad/LogicadBidder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package org.prebid.server.bidder.logicad;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import io.vertx.core.http.HttpMethod;
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.BidderError;
import org.prebid.server.bidder.model.HttpCall;
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.logicad.ExtImpLogicad;
import org.prebid.server.proto.openrtb.ext.response.BidType;
import org.prebid.server.util.HttpUtil;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
* Logicad {@link Bidder} implementation.
*/
public class LogicadBidder implements Bidder<BidRequest> {

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

private final String endpointUrl;
private final JacksonMapper mapper;

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

@Override
public Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest request) {
final List<BidderError> errors = new ArrayList<>();
final List<HttpRequest<BidRequest>> httpRequests = new ArrayList<>();
try {
final Map<ExtImpLogicad, List<Imp>> impToExtImp = getExtImpToImps(request, errors);
httpRequests.addAll(buildAdapterRequests(request, impToExtImp));
} catch (PreBidException e) {
return Result.of(Collections.emptyList(), errors);
}

return Result.of(httpRequests, errors);
}

private Map<ExtImpLogicad, List<Imp>> getExtImpToImps(BidRequest request, List<BidderError> errors) {
final Map<ExtImpLogicad, List<Imp>> result = new HashMap<>();
for (Imp imp : request.getImp()) {
try {
final ExtImpLogicad extImpLogicad = parseAndValidateImpExt(imp);
final Imp updatedImp = updateImp(imp, extImpLogicad.getTid());

result.putIfAbsent(extImpLogicad, new ArrayList<>());
result.get(extImpLogicad).add(updatedImp);
} catch (PreBidException e) {
errors.add(BidderError.badInput(e.getMessage()));
}
}

if (result.isEmpty()) {
throw new PreBidException("No appropriate impressions");
}

return result;
}

private ExtImpLogicad parseAndValidateImpExt(Imp imp) {
final ExtImpLogicad extImp;
try {
extImp = mapper.mapper().convertValue(imp.getExt(), LOGICAD_EXT_TYPE_REFERENCE).getBidder();
} catch (IllegalArgumentException e) {
throw new PreBidException(e.getMessage(), e);
}

if (StringUtils.isBlank(extImp.getTid())) {
throw new PreBidException("No tid value provided");
}

return extImp;
}

private static Imp updateImp(Imp imp, String tid) {
return imp.toBuilder().tagid(tid).ext(null).build();
}

private List<HttpRequest<BidRequest>> buildAdapterRequests(BidRequest bidRequest,
Map<ExtImpLogicad, List<Imp>> extImpToImps) {
final List<HttpRequest<BidRequest>> httpRequests = new ArrayList<>();

for (Map.Entry<ExtImpLogicad, List<Imp>> entry : extImpToImps.entrySet()) {
final BidRequest updatedBidRequest = bidRequest.toBuilder().imp(entry.getValue()).build();
final String body = mapper.encode(updatedBidRequest);

final HttpRequest<BidRequest> createdBidRequest = HttpRequest.<BidRequest>builder()
.method(HttpMethod.POST)
.uri(endpointUrl)
.body(body)
.headers(HttpUtil.headers())
.payload(bidRequest)
.build();

httpRequests.add(createdBidRequest);
}

return httpRequests;
}

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

private static List<BidderBid> extractBids(BidResponse bidResponse) {
if (bidResponse == null || bidResponse.getSeatbid() == null) {
return Collections.emptyList();
}
return bidsFromResponse(bidResponse);
}

private static List<BidderBid> bidsFromResponse(BidResponse bidResponse) {
return bidResponse.getSeatbid().stream()
.map(SeatBid::getBid)
.flatMap(Collection::stream)
.map(bid -> BidderBid.of(bid, BidType.banner, bidResponse.getCur()))
.collect(Collectors.toList());
}

@Override
public Map<String, String> extractTargeting(ObjectNode ext) {
return Collections.emptyMap();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.prebid.server.proto.openrtb.ext.request.logicad;

import lombok.AllArgsConstructor;
import lombok.Value;

/**
* Defines the contract for bidRequest.imp[i].ext.logicad
*/
@AllArgsConstructor(staticName = "of")
@Value
public class ExtImpLogicad {

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

import org.prebid.server.bidder.BidderDeps;
import org.prebid.server.bidder.logicad.LogicadBidder;
import org.prebid.server.json.JacksonMapper;
import org.prebid.server.spring.config.bidder.model.BidderConfigurationProperties;
import org.prebid.server.spring.config.bidder.model.UsersyncConfigurationProperties;
import org.prebid.server.spring.config.bidder.util.BidderDepsAssembler;
import org.prebid.server.spring.config.bidder.util.BidderInfoCreator;
import org.prebid.server.spring.config.bidder.util.UsersyncerCreator;
import org.prebid.server.spring.env.YamlPropertySourceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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 javax.validation.constraints.NotBlank;

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

private static final String BIDDER_NAME = "logicad";

@Value("${external-url}")
@NotBlank
private String externalUrl;

@Autowired
private JacksonMapper mapper;

@Autowired
@Qualifier("logicadConfigurationProperties")
private BidderConfigurationProperties configProperties;

@Bean("logicadConfigurationProperties")
@ConfigurationProperties("adapters.logicad")
BidderConfigurationProperties configurationProperties() {
return new BidderConfigurationProperties();
}

@Bean
BidderDeps logicadBidderDeps() {
final UsersyncConfigurationProperties usersync = configProperties.getUsersync();

return BidderDepsAssembler.forBidder(BIDDER_NAME)
.withConfig(configProperties)
.bidderInfo(BidderInfoCreator.create(configProperties))
.usersyncerCreator(UsersyncerCreator.create(usersync, externalUrl))
.bidderCreator(() -> new LogicadBidder(configProperties.getEndpoint(), mapper))
.assemble();
}
}
23 changes: 23 additions & 0 deletions src/main/resources/bidder-config/logicad.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
adapters:
logicad:
enabled: false
endpoint: https://pbs.ladsp.com/adrequest/prebidserver
pbs-enforces-gdpr: true
pbs-enforces-ccpa: true
modifying-vast-xml-allowed: true
deprecated-names:
aliases:
meta-info:
maintainer-email: prebid@so-netmedia.jp
app-media-types:
- banner
site-media-types:
- banner
supported-vendors:
vendor-id: 14
usersync:
url: https://cr-p31.ladsp.jp/cookiesender/31?r=true&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&ru=
redirect-url: /setuid?bidder=logicad&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&us_privacy={{us_privacy}}&uid=$UID
cookie-family-name: logicad
type: redirect
support-cors: false
13 changes: 13 additions & 0 deletions src/main/resources/static/bidder-params/logicad.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Logicad Adapter Params",
"description": "A schema which validates params accepted by the Logicad adapter",
"type": "object",
"properties": {
"tid": {
"type": "string",
"description": "Logicad for Publishers placement ID"
}
},
"required": ["tid"]
}
Loading

0 comments on commit fa3733a

Please sign in to comment.