Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PG: Add more information to LineItemStatusReport. #1898

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ public LineItemStatusReport getLineItemStatusReport(String lineItemId) {
.readyToServeTimestamp(lineItem.getReadyAt())
.spentTokens(activeDeliveryPlan.getSpentTokens())
.pacingFrequency(activeDeliveryPlan.getDeliveryRateInMilliseconds())
.accountId(lineItem.getAccountId())
.target(lineItem.getTargeting())
.build();
}
}
5 changes: 5 additions & 0 deletions src/main/java/org/prebid/server/deals/lineitem/LineItem.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.prebid.server.deals.lineitem;

import com.fasterxml.jackson.databind.node.ObjectNode;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import org.apache.commons.collections4.CollectionUtils;
Expand Down Expand Up @@ -189,6 +190,10 @@ public List<LineItemSize> getSizes() {
return metaData.getSizes();
}

public ObjectNode getTargeting() {
return metaData.getTargeting();
}

public TargetingDefinition getTargetingDefinition() {
return targetingDefinition;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.prebid.server.deals.proto.report;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Builder;
import lombok.Value;

Expand All @@ -24,4 +25,9 @@ public class LineItemStatusReport {

@JsonProperty("pacingFrequency")
Long pacingFrequency;

@JsonProperty("accountId")
String accountId;

ObjectNode target;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.prebid.server.functional.model.deals.report

import groovy.transform.ToString

import java.time.ZonedDateTime

@ToString(includeNames = true, ignoreNulls = true)
class LineItemStatusReport {

String lineItemId
DeliverySchedule deliverySchedule
Long spentTokens
ZonedDateTime readyToServeTimestamp
Long pacingFrequency
String accountId
Map target
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.restassured.response.Response
import io.restassured.specification.RequestSpecification
import org.prebid.server.functional.model.UidsCookie
import org.prebid.server.functional.model.bidder.BidderName
import org.prebid.server.functional.model.deals.report.LineItemStatusReport
import org.prebid.server.functional.model.mock.services.prebidcache.response.PrebidCacheResponse
import org.prebid.server.functional.model.request.amp.AmpRequest
import org.prebid.server.functional.model.request.auction.BidRequest
Expand Down Expand Up @@ -58,6 +59,7 @@ class PrebidServerService implements ObjectMapperWrapper {
static final String HTTP_INTERACTION_ENDPOINT = "/logging/httpinteraction"
static final String COLLECTED_METRICS_ENDPOINT = "/collected-metrics"
static final String FORCE_DEALS_UPDATE_ENDPOINT = "/pbs-admin/force-deals-update"
static final String LINE_ITEM_STATUS_ENDPOINT = "/pbs-admin/lineitem-status"
static final String PROMETHEUS_METRICS_ENDPOINT = "/metrics"

private final PrebidServerContainer pbsContainer
Expand Down Expand Up @@ -261,6 +263,19 @@ class PrebidServerService implements ObjectMapperWrapper {
checkResponseStatusCode(response, 204)
}

@Step("[GET] /pbs-admin/lineitem-status")
LineItemStatusReport sendLineItemStatusRequest(String lineItemId) {
def request = given(adminRequestSpecification)
if (lineItemId != null) {
request.queryParam("id", lineItemId)
}

def response = request.get(LINE_ITEM_STATUS_ENDPOINT)

checkResponseStatusCode(response)
response.as(LineItemStatusReport)
}

@Step("[GET] /metrics")
String sendPrometheusMetricsRequest() {
def response = given(prometheusRequestSpecification).get(PROMETHEUS_METRICS_ENDPOINT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class PbsPgConfig {
private static Map<String, String> getPgConfig(String networkServiceContainerUri) {
pbsGeneralSettings() + adminDealsUpdateEndpoint() + deals() + deliveryProgress() +
planner(networkServiceContainerUri) + deliveryStatistics(networkServiceContainerUri) +
alert(networkServiceContainerUri) + userData(networkServiceContainerUri)
alert(networkServiceContainerUri) + userData(networkServiceContainerUri) + adminLineItemStatusEndpoint()
}

private static Map<String, String> pbsGeneralSettings() {
Expand All @@ -70,6 +70,10 @@ class PbsPgConfig {
["admin-endpoints.force-deals-update.enabled": "true"]
}

private static Map<String, String> adminLineItemStatusEndpoint() {
["admin-endpoints.lineitem-status.enabled": "true"]
}

private static Map<String, String> deals() {
["deals.enabled" : "true",
"deals.simulation.enabled" : "false",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package org.prebid.server.functional.tests.pg

import org.prebid.server.functional.model.deals.lineitem.DeliverySchedule
import org.prebid.server.functional.model.deals.lineitem.LineItem
import org.prebid.server.functional.model.deals.lineitem.Token
import org.prebid.server.functional.model.mock.services.generalplanner.PlansResponse
import org.prebid.server.functional.model.request.auction.BidRequest
import org.prebid.server.functional.model.request.dealsupdate.ForceDealsUpdateRequest
import org.prebid.server.functional.model.response.auction.BidResponse
import org.prebid.server.functional.service.PrebidServerException
import org.prebid.server.functional.util.ObjectMapperWrapper
import org.prebid.server.functional.util.PBSUtils

import java.time.ZoneId
import java.time.ZonedDateTime

import static java.time.ZoneOffset.UTC
import static java.time.temporal.ChronoUnit.SECONDS

class LineItemStatusSpec extends BasePgSpec implements ObjectMapperWrapper {

def cleanup() {
pgPbsService.sendForceDealsUpdateRequest(ForceDealsUpdateRequest.invalidateLineItemsRequest)
}

def "PBS should return a bad request exception when no 'id' query parameter is provided"() {
when: "Requesting endpoint without 'id' parameter"
pgPbsService.sendLineItemStatusRequest(null)

then: "PBS throws an exception"
def exception = thrown(PrebidServerException)
assert exception.statusCode == 400
assert exception.responseBody.contains("id parameter is required")
}

def "PBS should return a bad request exception when endpoint is requested with not existing line item id"() {
given: "Not existing line item id"
def notExistingLineItemId = PBSUtils.randomString

when: "Requesting endpoint"
pgPbsService.sendLineItemStatusRequest(notExistingLineItemId)

then: "PBS throws an exception"
def exception = thrown(PrebidServerException)
assert exception.statusCode == 400
assert exception.responseBody.contains("LineItem not found: $notExistingLineItemId")
}

def "PBS should return an empty line item status response when line item doesn't have a scheduled delivery"() {
given: "Line item with no delivery schedule"
def plansResponse = PlansResponse.getDefaultPlansResponse(PBSUtils.randomString).tap {
lineItems[0].deliverySchedules = null
}
def lineItemId = plansResponse.lineItems[0].lineItemId
generalPlanner.initPlansResponse(plansResponse)

and: "Line items are fetched by PBS"
updateLineItemsAndWait()

when: "Requesting endpoint"
def lineItemStatusReport = pgPbsService.sendLineItemStatusRequest(lineItemId)

then: "Empty line item status report is returned"
verifyAll(lineItemStatusReport) {
it.lineItemId == lineItemId
!it.deliverySchedule
!it.spentTokens
!it.readyToServeTimestamp
!it.pacingFrequency
!it.accountId
!it.target
}
}

def "PBS should return filled line item status report when line item has a scheduled delivery"() {
given: "Line item with a scheduled delivery"
def plansResponse = new PlansResponse(lineItems: [LineItem.getDefaultLineItem(PBSUtils.randomString).tap {
deliverySchedules = [DeliverySchedule.defaultDeliverySchedule]
}])
generalPlanner.initPlansResponse(plansResponse)
def lineItemId = plansResponse.lineItems[0].lineItemId
def lineItem = plansResponse.lineItems[0]
def deliverySchedule = lineItem.deliverySchedules[0]

and: "Line items are fetched by PBS"
updateLineItemsAndWait()

when: "Requesting endpoint"
def lineItemStatusReport = pgPbsService.sendLineItemStatusRequest(lineItemId)

then: "Line item status report is returned"
def reportTimeZone = lineItemStatusReport.deliverySchedule?.planStartTimeStamp?.zone
assert reportTimeZone

verifyAll(lineItemStatusReport) {
it.lineItemId == lineItemId
it.deliverySchedule?.planId == deliverySchedule.planId

it.deliverySchedule.planStartTimeStamp == timeToReportFormat(deliverySchedule.startTimeStamp, reportTimeZone)
it.deliverySchedule?.planExpirationTimeStamp == timeToReportFormat(deliverySchedule.endTimeStamp, reportTimeZone)
it.deliverySchedule?.planUpdatedTimeStamp == timeToReportFormat(deliverySchedule.updatedTimeStamp, reportTimeZone)

it.deliverySchedule?.tokens?.size() == deliverySchedule.tokens.size()
it.deliverySchedule?.tokens?.first()?.priorityClass == deliverySchedule.tokens[0].priorityClass
it.deliverySchedule?.tokens?.first()?.total == deliverySchedule.tokens[0].total
!it.deliverySchedule?.tokens?.first()?.spent
!it.deliverySchedule?.tokens?.first()?.totalSpent

it.spentTokens == 0
it.readyToServeTimestamp.isBefore(ZonedDateTime.now())
it.pacingFrequency == getDeliveryRateMs(deliverySchedule)
it.accountId == lineItem.accountId
encode(it.target) == encode(lineItem.targeting)
}
}

def "PBS should return line item status report with an active scheduled delivery"() {
given: "Line item with an active and expired scheduled deliveries"
def plansResponse = new PlansResponse(lineItems: [LineItem.getDefaultLineItem(PBSUtils.randomString).tap {
deliverySchedules = [DeliverySchedule.defaultDeliverySchedule.tap {
startTimeStamp = ZonedDateTime.now(ZoneId.from(UTC)).minusHours(12)
updatedTimeStamp = ZonedDateTime.now(ZoneId.from(UTC)).minusHours(12)
endTimeStamp = ZonedDateTime.now(ZoneId.from(UTC)).minusHours(6)
}, DeliverySchedule.defaultDeliverySchedule.tap {
startTimeStamp = ZonedDateTime.now(ZoneId.from(UTC))
updatedTimeStamp = ZonedDateTime.now(ZoneId.from(UTC))
endTimeStamp = ZonedDateTime.now(ZoneId.from(UTC)).plusHours(12)
}]
}])
generalPlanner.initPlansResponse(plansResponse)
def lineItemId = plansResponse.lineItems[0].lineItemId
def activeDeliverySchedule = plansResponse.lineItems[0].deliverySchedules[1]

and: "Line items are fetched by PBS"
updateLineItemsAndWait()

when: "Requesting endpoint"
def lineItemStatusReport = pgPbsService.sendLineItemStatusRequest(lineItemId)

then: "Line item status report is returned with an active delivery"
assert lineItemStatusReport.lineItemId == lineItemId
assert lineItemStatusReport.deliverySchedule?.planId == activeDeliverySchedule.planId
}

def "PBS should return line item status report with increased spent token number when PG auction has happened"() {
given: "Bid request"
def bidRequest = BidRequest.defaultBidRequest

and: "Line item with a scheduled delivery"
def plansResponse = new PlansResponse(lineItems: [LineItem.getDefaultLineItem(bidRequest.site.publisher.id).tap {
deliverySchedules = [DeliverySchedule.defaultDeliverySchedule.tap {
tokens = [Token.defaultToken]
}]
}])
generalPlanner.initPlansResponse(plansResponse)
def lineItemId = plansResponse.lineItems[0].lineItemId
def spentTokensNumber = 1

and: "Line items are fetched by PBS"
updateLineItemsAndWait()

and: "PG bid response is set"
def bidResponse = BidResponse.getDefaultPgBidResponse(bidRequest, plansResponse)
bidder.setResponse(bidRequest.id, bidResponse)

and: "PBS PG auction is requested"
pgPbsService.sendAuctionRequest(bidRequest)

when: "Requesting line item status endpoint"
def lineItemStatusReport = pgPbsService.sendLineItemStatusRequest(lineItemId)

then: "Spent token number in line item status report is increased"
assert lineItemStatusReport.lineItemId == lineItemId
assert lineItemStatusReport.spentTokens == spentTokensNumber
assert lineItemStatusReport.deliverySchedule?.tokens?.first()?.spent == spentTokensNumber
}

private ZonedDateTime timeToReportFormat(ZonedDateTime givenTime, ZoneId reportTimeZone) {
givenTime.truncatedTo(SECONDS).withZoneSameInstant(reportTimeZone)
}

private Integer getDeliveryRateMs(DeliverySchedule deliverySchedule) {
deliverySchedule.tokens[0].total > 0
? (deliverySchedule.endTimeStamp.toInstant().toEpochMilli()
- deliverySchedule.startTimeStamp.toInstant().toEpochMilli()) /
deliverySchedule.tokens[0].total
: null
}
}
Loading