Skip to content

Commit

Permalink
Create mgid bidder (prebid#383)
Browse files Browse the repository at this point in the history
  • Loading branch information
DGarbar authored and rpanchyk committed Jul 5, 2019
1 parent 5f7c8b7 commit 0c04b33
Show file tree
Hide file tree
Showing 15 changed files with 952 additions and 0 deletions.
181 changes: 181 additions & 0 deletions src/main/java/org/prebid/server/bidder/mgid/MgidBidder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package org.prebid.server.bidder.mgid;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.request.BidRequest;
import com.iab.openrtb.request.Imp;
import com.iab.openrtb.request.Imp.ImpBuilder;
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.DecodeException;
import io.vertx.core.json.Json;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.prebid.server.bidder.Bidder;
import org.prebid.server.bidder.BidderUtil;
import org.prebid.server.bidder.mgid.model.ExtBidMgid;
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.proto.openrtb.ext.request.mgid.ExtImpMgid;
import org.prebid.server.proto.openrtb.ext.response.BidType;
import org.prebid.server.util.HttpUtil;

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

public class MgidBidder implements Bidder<BidRequest> {

private static final String DEFAULT_BID_CURRENCY = "USD";
private final String endpointUrl;

public MgidBidder(String endpoint) {
endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpoint));
}

@Override
public final Result<List<HttpRequest<BidRequest>>> makeHttpRequests(BidRequest bidRequest) {
final List<Imp> imps = new ArrayList<>();
String accountId = null;
for (Imp imp : bidRequest.getImp()) {
try {
final ExtImpMgid impExt = parseImpExt(imp);

if (StringUtils.isBlank(accountId) && StringUtils.isNotBlank(impExt.getAccountId())) {
accountId = impExt.getAccountId();
}

final Imp modifiedImp = modifyImp(imp, impExt);
imps.add(modifiedImp);
} catch (PreBidException e) {
return Result.emptyWithError(BidderError.badInput(e.getMessage()));
}
}

if (StringUtils.isBlank(accountId)) {
return Result.emptyWithError(BidderError.badInput("accountId is not set"));
}

final BidRequest outgoingRequest = bidRequest.toBuilder()
.tmax(bidRequest.getTmax())
.imp(imps)
.build();

final String body = Json.encode(outgoingRequest);

return Result.of(Collections.singletonList(HttpRequest.<BidRequest>builder()
.method(HttpMethod.POST)
.uri(endpointUrl + accountId)
.body(body)
.headers(BidderUtil.headers())
.payload(outgoingRequest)
.build()), Collections.emptyList());
}

private static ExtImpMgid parseImpExt(Imp imp) {
try {
return Json.mapper.convertValue(imp.getExt().get("bidder"), ExtImpMgid.class);
} catch (IllegalArgumentException e) {
throw new PreBidException(e.getMessage(), e);
}
}

private static Imp modifyImp(Imp imp, ExtImpMgid impExt) throws PreBidException {
final ImpBuilder impBuilder = imp.toBuilder();

final String cur = getCur(impExt);
if (StringUtils.isNotBlank(cur)) {
impBuilder.bidfloorcur(cur);
}

final BigDecimal bidFloor = getBidFloor(impExt);
if (bidFloor != null) {
impBuilder.bidfloor(bidFloor);
}

return impBuilder
.tagid(impExt.getPlacementId())
.build();
}

private static String getCur(ExtImpMgid impMgid) {
return ObjectUtils.defaultIfNull(
currencyValueOrNull(impMgid.getCurrency()), currencyValueOrNull(impMgid.getCur()));
}

private static BigDecimal getBidFloor(ExtImpMgid impMgid) {
return ObjectUtils.defaultIfNull(
bidFloorValueOrNull(impMgid.getBidfloor()), bidFloorValueOrNull(impMgid.getBidFloorSecond()));
}

private static String currencyValueOrNull(String value) {
return StringUtils.isNotBlank(value) && !value.equals("USD") ? value : null;
}

private static BigDecimal bidFloorValueOrNull(BigDecimal value) {
return value != null && value.compareTo(BigDecimal.ZERO) > 0 ? value : null;
}

@Override
public final Result<List<BidderBid>> makeBids(HttpCall<BidRequest> httpCall,
BidRequest bidRequest) {
try {
final BidResponse bidResponse = Json
.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()
.filter(Objects::nonNull)
.map(SeatBid::getBid)
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.map(bid -> BidderBid.of(bid, getBidType(bid), DEFAULT_BID_CURRENCY))
.collect(Collectors.toList());
}

private static BidType getBidType(Bid bid) {
final ExtBidMgid bidExt = getBidExt(bid);
if (bidExt == null) {
return BidType.banner;
}

final BidType crtype = bidExt.getCrtype();
return crtype == null ? BidType.banner : crtype;
}

private static ExtBidMgid getBidExt(Bid bid) {
try {
return Json.mapper.convertValue(bid.getExt(), ExtBidMgid.class);
} catch (IllegalArgumentException e) {
return null;
}
}

@Override
public final Map<String, String> extractTargeting(ObjectNode ext) {
return Collections.emptyMap();
}
}
12 changes: 12 additions & 0 deletions src/main/java/org/prebid/server/bidder/mgid/model/ExtBidMgid.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.prebid.server.bidder.mgid.model;

import lombok.AllArgsConstructor;
import lombok.Value;
import org.prebid.server.proto.openrtb.ext.response.BidType;

@Value
@AllArgsConstructor(staticName = "of")
public class ExtBidMgid {

BidType crtype;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.prebid.server.proto.openrtb.ext.request.mgid;

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

import java.math.BigDecimal;

/**
* Defines the contract for bidRequest.imp[i].ext.mgid
*/
@AllArgsConstructor(
staticName = "of"
)
@Value
public class ExtImpMgid {
@JsonProperty("accountId")
String accountId;

@JsonProperty("placementId")
String placementId;

String cur;

String currency;

BigDecimal bidfloor;

@JsonProperty("bidFloor")
BigDecimal bidFloorSecond;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.prebid.server.spring.config.bidder;

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

private static final String BIDDER_NAME = "mgid";

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

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

@Bean("mgidConfigurationProperties")
@ConfigurationProperties("adapters.mgid")
BidderConfigurationProperties configurationProperties() {
return new BidderConfigurationProperties();
}

@Bean
BidderDeps mgidBidderDeps() {
return BidderDepsAssembler.forBidder(BIDDER_NAME)
.withConfig(configProperties)
.bidderInfo(BidderInfoCreator.create(configProperties))
.usersyncerCreator(UsersyncerCreator.create(configProperties.getUsersync(), externalUrl))
.bidderCreator(() -> new MgidBidder(configProperties.getEndpoint()))
.assemble();
}
}
23 changes: 23 additions & 0 deletions src/main/resources/bidder-config/mgid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
adapters:
mgid:
enabled: false
endpoint: https://prebid.mgid.com/prebid/
pbs-enforces-gdpr: true
deprecated-names:
aliases:
meta-info:
maintainer-email: [email protected]
app-media-types:
- banner
- native
site-media-types:
- banner
- native
supported-vendors:
vendor-id: 358
usersync:
url: https://cm.mgid.com/m?cdsp=363893&adu=
redirect-url: /setuid?bidder=mgid&gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&uid={muidn}
cookie-family-name: mgid
type: redirect
support-cors: false
33 changes: 33 additions & 0 deletions src/main/resources/static/bidder-params/mgid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Mgid Params",
"description": "A schema which validates params accepted by the Mgid",
"type": "object",
"properties": {
"accountId": {
"type": "string",
"description": "Internal Mgid account ID"
},
"placementId": {
"type": "string",
"description": "Internal Mgid Placement ID"
},
"cur": {
"type": "string",
"description": "optional bidfloor currency"
},
"currency": {
"type": "string",
"description": "optional bidfloor currency"
},
"bidfloor": {
"type": "number",
"description": "optional minimum acceptable bid, in CPM, USD by default"
},
"bidFloor": {
"type": "number",
"description": "optional minimum acceptable bid, in CPM, USD by default"
}
},
"required": ["accountId","placementId"]
}
Loading

0 comments on commit 0c04b33

Please sign in to comment.