Skip to content

Commit

Permalink
New Adapter: AlgoriX (#1275)
Browse files Browse the repository at this point in the history
* add AlgoriX Bidder Adapter

* fix email

* fixup for endpoints and fix bug for LGTM

* fix code style

* update fix AlgoriX Test

* fix code style from code review

* fix Test from code Review

* fix code style check

* fix mvn check failure

* fix check test failure

* fix check failure in AlgorixTest for test-acution

* Fetch prebid:master and fix change Test Code

* fix with comments

* change call order from comments

Co-authored-by: 寻云波 <xunyunbo@do-global.com>
  • Loading branch information
2 people authored and nickluck8 committed Aug 10, 2021
1 parent 51da700 commit 97950d7
Show file tree
Hide file tree
Showing 14 changed files with 867 additions and 0 deletions.
196 changes: 196 additions & 0 deletions src/main/java/org/prebid/server/bidder/algorix/AlgorixBidder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package org.prebid.server.bidder.algorix;

import com.fasterxml.jackson.core.type.TypeReference;
import com.iab.openrtb.request.Banner;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Format;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpMethod;
import org.apache.commons.collections4.CollectionUtils;
import org.prebid.server.bidder.Bidder;
import org.prebid.server.bidder.model.BidderError;
import org.prebid.server.bidder.model.BidderBid;
import org.prebid.server.bidder.model.HttpRequest;
import org.prebid.server.bidder.model.HttpCall;
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.algorix.ExtImpAlgorix;
import org.prebid.server.proto.openrtb.ext.response.BidType;
import org.prebid.server.util.HttpUtil;

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

/**
* AlgoriX (@link Bidder) implementation.
*/
public class AlgorixBidder implements Bidder<BidRequest> {

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

private static final String URL_SID_MACRO = "{SID}";
private static final String URL_TOKEN_MACRO = "{TOKEN}";

private static final int FIRST_INDEX = 0;

private final String endpointUrl;
private final JacksonMapper mapper;

public AlgorixBidder(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<Imp> updatedImps = new ArrayList<>();

ExtImpAlgorix extImpAlgorix = null;

for (Imp imp : request.getImp()) {
try {
extImpAlgorix = extImpAlgorix == null ? parseImpExt(imp) : extImpAlgorix;
updatedImps.add(updateImp(imp));
} catch (PreBidException error) {
errors.add(BidderError.badInput(error.getMessage()));
}
}

if (extImpAlgorix == null) {
return Result.withError(BidderError.badInput("Invalid ExtImpAlgoriX value"));
}

final BidRequest outgoingRequest = request.toBuilder().imp(updatedImps).build();
final String body = mapper.encode(outgoingRequest);
return Result.of(Collections.singletonList(
HttpRequest.<BidRequest>builder()
.method(HttpMethod.POST)
.uri(resolveUrl(endpointUrl, extImpAlgorix))
.headers(resolveHeaders())
.payload(outgoingRequest)
.body(body)
.build()),
errors);
}

/**
* Parse Ext Imp
* @param imp BidRequest Imp
* @return Algorix Ext Imp
*/
private ExtImpAlgorix parseImpExt(Imp imp) {
try {
return mapper.mapper().convertValue(imp.getExt(), ALGORIX_EXT_TYPE_REFERENCE).getBidder();
} catch (IllegalArgumentException error) {
throw new PreBidException(String.format("Impression Id=%s, has invalid Ext", imp.getId()));
}
}

/**
* Update Imp for transform banner Size
* @param imp imp
* @return new imp
*/
private static Imp updateImp(Imp imp) {
if (imp.getBanner() != null) {
final Banner banner = imp.getBanner();
if (!(isValidSizeValue(banner.getW()) && isValidSizeValue(banner.getH()))
&& CollectionUtils.isNotEmpty(banner.getFormat())) {
final Format firstFormat = banner.getFormat().get(FIRST_INDEX);
return imp.toBuilder()
.banner(banner.toBuilder()
.w(firstFormat.getW())
.h(firstFormat.getH())
.build())
.build();
}
}
return imp;
}

/**
* Check Integer Size Value is Valid(not null and no zero)
* @param value Integer size value
* @return true or false
*/
private static boolean isValidSizeValue(Integer value) {
return value != null && value > 0;
}

/**
* Replace url macro
* @param endpoint endpoint Url
* @param extImp Algorix Ext Imp
* @return target Url
*/
private static String resolveUrl(String endpoint, ExtImpAlgorix extImp) {
return endpoint
.replace(URL_SID_MACRO, extImp.getSid())
.replace(URL_TOKEN_MACRO, extImp.getToken());
}

/**
* Add openrtb version header 2.5
* @return headers
*/
private static MultiMap resolveHeaders() {
final MultiMap headers = HttpUtil.headers();
headers.add(HttpUtil.X_OPENRTB_VERSION_HEADER, "2.5");
return headers;
}

@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(httpCall.getRequest().getPayload(), bidResponse), Collections.emptyList());
} catch (DecodeException | PreBidException e) {
return Result.withError(BidderError.badServerResponse(e.getMessage()));
}
}

private List<BidderBid> extractBids(BidRequest bidRequest, BidResponse bidResponse) {
if (bidResponse == null || CollectionUtils.isEmpty(bidResponse.getSeatbid())) {
return Collections.emptyList();
}
return bidsFromResponse(bidRequest, bidResponse);
}

private static List<BidderBid> bidsFromResponse(BidRequest bidRequest, BidResponse bidResponse) {
return bidResponse.getSeatbid().stream()
.filter(Objects::nonNull)
.map(SeatBid::getBid)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.map(bid -> BidderBid.of(bid, getBidType(bid.getImpid(), bidRequest.getImp()), bidResponse.getCur()))
.collect(Collectors.toList());
}

private static BidType getBidType(String impId, List<Imp> imps) {
for (Imp imp : imps) {
if (imp.getId().equals(impId)) {
if (imp.getBanner() != null) {
return BidType.banner;
} else if (imp.getVideo() != null) {
return BidType.video;
} else if (imp.getXNative() != null) {
return BidType.xNative;
}
}
}
return BidType.banner;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.prebid.server.proto.openrtb.ext.request.algorix;

import lombok.AllArgsConstructor;
import lombok.Value;

/**
* Algorix Ext Imp
*/
@AllArgsConstructor(staticName = "of")
@Value
public class ExtImpAlgorix {

String sid;

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

import org.prebid.server.bidder.BidderDeps;
import org.prebid.server.bidder.algorix.AlgorixBidder;
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.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/algorix.yaml",
factory = YamlPropertySourceFactory.class)
public class AlgorixConfiguration {

private static final String BIDDER_NAME = "algorix";

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

@Autowired
private JacksonMapper mapper;

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

@Bean("algorixConfigurationProperties")
@ConfigurationProperties("adapters.algorix")
BidderConfigurationProperties configurationProperties() {
return new BidderConfigurationProperties();
}

@Bean
BidderDeps algorixBidderDeps() {
return BidderDepsAssembler.forBidder(BIDDER_NAME)
.withConfig(configProperties)
.usersyncerCreator(UsersyncerCreator.create(externalUrl))
.bidderCreator(config -> new AlgorixBidder(config.getEndpoint(), mapper))
.assemble();
}

}
24 changes: 24 additions & 0 deletions src/main/resources/bidder-config/algorix.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
adapters:
algorix:
enabled: false
endpoint: https://xyz.svr-algorix.com/rtb/sa?sid={SID}&token={TOKEN}
pbs-enforces-gdpr: true
pbs-enforces-ccpa: true
modifying-vast-xml-allowed: true
deprecated-names:
aliases: {}
meta-info:
maintainer-email: xunyunbo@algorix.co
app-media-types:
- banner
- video
- native
site-media-types:
supported-vendors:
vendor-id: 0
usersync:
url:
redirect-url:
cookie-family-name: algorix
type: redirect
support-cors: false
17 changes: 17 additions & 0 deletions src/main/resources/static/bidder-params/algorix.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "AlgoriX Adapter Params",
"description": "A schema which validates params accepted by the AlgoriX adapter",
"type": "object",
"properties": {
"sid": {
"type": "string",
"description": "Your Sid"
},
"token": {
"type": "string",
"description": "Your Token"
}
},
"required": ["sid", "token"]
}
Loading

0 comments on commit 97950d7

Please sign in to comment.