Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,41 @@
import org.prebid.server.proto.openrtb.ext.ExtPrebid;
import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebid;
import org.prebid.server.proto.openrtb.ext.request.ExtRequestPrebidServer;
import org.prebid.server.proto.openrtb.ext.request.ExtStoredRequest;
import org.prebid.server.proto.openrtb.ext.request.nextmillennium.ExtImpNextMillennium;
import org.prebid.server.proto.openrtb.ext.request.nextmillennium.ExtRequestNextMillennium;
import org.prebid.server.proto.openrtb.ext.response.BidType;
import org.prebid.server.util.HttpUtil;
import org.prebid.server.util.ObjectUtil;
import org.prebid.server.version.PrebidVersionProvider;

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

public class NextMillenniumBidder implements Bidder<BidRequest> {

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

private static final String NM_ADAPTER_VERSION = "v1.0.0";

private final String endpointUrl;
private final JacksonMapper mapper;
private final List<String> nmmFlags;
private final PrebidVersionProvider versionProvider;

public NextMillenniumBidder(String endpointUrl, JacksonMapper mapper, List<String> nmmFlags) {
public NextMillenniumBidder(String endpointUrl,
JacksonMapper mapper,
List<String> nmmFlags,
PrebidVersionProvider versionProvider) {
this.endpointUrl = HttpUtil.validateUrl(Objects.requireNonNull(endpointUrl));
this.mapper = Objects.requireNonNull(mapper);
this.nmmFlags = nmmFlags;

this.versionProvider = Objects.requireNonNull(versionProvider);
}

@Override
Expand Down Expand Up @@ -87,26 +95,34 @@ private ExtImpNextMillennium convertExt(ObjectNode impExt) {
private BidRequest updateBidRequest(BidRequest bidRequest, ExtImpNextMillennium ext) {
final ExtStoredRequest storedRequest = ExtStoredRequest.of(resolveStoredRequestId(bidRequest, ext));

final ExtRequest requestExt = bidRequest.getExt();
final ExtRequestPrebid existingPrebid = requestExt != null ? requestExt.getPrebid() : null;
final ExtRequestPrebidServer existingServer = existingPrebid != null ? existingPrebid.getServer() : null;

final ExtRequestPrebid createdExtRequestPrebid = ExtRequestPrebid.builder()
.storedrequest(storedRequest)
.server(existingServer)
.build();

final ExtRequestPrebid extRequestPrebid = Optional.ofNullable(bidRequest)
.map(BidRequest::getExt)
.map(ExtRequest::getPrebid)
.map(prebid -> prebid.toBuilder().storedrequest(storedRequest).build())
.orElse(createdExtRequestPrebid);
final ExtRequest extRequest = ExtRequest.of(createdExtRequestPrebid);

return bidRequest.toBuilder()
.imp(updateImps(bidRequest, createdExtRequestPrebid))
.ext(ExtRequest.of(extRequestPrebid))
final ObjectNode nextMillenniumNode = mapper.mapper().valueToTree(
ExtRequestNextMillennium.of(nmmFlags, NM_ADAPTER_VERSION, versionProvider.getNameVersionRecord()));

extRequest.addProperty("next_millennium", nextMillenniumNode);
Copy link
Collaborator

Choose a reason for hiding this comment

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

incorrect property name, should be nextMillennium


final Imp firstImp = bidRequest.getImp().getFirst();
final ObjectNode updatedImpExt = mapper.mapper().createObjectNode();
updatedImpExt.set("bidder", mapper.mapper().valueToTree(ext));
Copy link
Collaborator

Choose a reason for hiding this comment

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

what is this? I don't see any bidder field in the bidder request

Copy link
Collaborator Author

@przemkaczmarek przemkaczmarek Mar 10, 2025

Choose a reason for hiding this comment

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

in L11-15 there is bidder field:

"ext": {
        "bidder": {
          "placement_id": "placement_id"
        }
      }

Copy link
Collaborator

Choose a reason for hiding this comment

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

the Go doesn't have it


final Imp updatedImp = firstImp.toBuilder()
.ext(updatedImpExt)
.build();
}

private List<Imp> updateImps(BidRequest bidRequest, ExtRequestPrebid extRequestPrebid) {
return bidRequest.getImp().stream()
.map(imp -> imp.toBuilder().ext(createImpExt(extRequestPrebid)).build())
.toList();
return bidRequest.toBuilder()
.imp(List.of(updatedImp))
Copy link
Collaborator

Choose a reason for hiding this comment

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

it should update only a first imp, but not replace the whole list of imps with a single one

.ext(extRequest)
.build();
}

private static String resolveStoredRequestId(BidRequest bidRequest, ExtImpNextMillennium extImpNextMillennium) {
Expand Down Expand Up @@ -146,16 +162,6 @@ private static String formatSize(Integer w, Integer h) {
: null;
}

private ObjectNode createImpExt(ExtRequestPrebid prebid) {
final ObjectNode impExt = mapper.mapper().createObjectNode();
impExt.set("prebid", mapper.mapper().valueToTree(prebid));
if (CollectionUtils.isNotEmpty(nmmFlags)) {
impExt.putObject("nextMillennium")
.set("nmmFlags", mapper.mapper().valueToTree(nmmFlags));
}
return impExt;
}

private HttpRequest<BidRequest> makeHttpRequest(BidRequest bidRequest) {
return HttpRequest.<BidRequest>builder()
.method(HttpMethod.POST)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.prebid.server.proto.openrtb.ext.request.nextmillennium;

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

import java.util.List;

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

@JsonProperty("nmm_flags")
List<String> nmmFlags;

@JsonProperty("nm_version")
Copy link
Collaborator

Choose a reason for hiding this comment

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

redundant

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think its not redutat because i got from server:
image

and how about "still incorrect, it should be nmmFlags" i have got n m m F l a g s . where is a problem?

Copy link
Collaborator

Choose a reason for hiding this comment

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

the annotation is redundant

i have got n m m F l a g s . where is a problem? Sorry, I didn't get. According to the Go the field should be serialized into the nnmFlags instead of nnm_flags

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

nnm or nmm ? I have got nmm in Go and Java
image

Copy link
Collaborator

@AntoxaAntoxic AntoxaAntoxic Mar 11, 2025

Choose a reason for hiding this comment

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

the point was about the case, it should be nmmFlags instead of the nmm_flags in the bidder request

String nmVersion;

@JsonProperty("server_version")
Copy link
Collaborator

Choose a reason for hiding this comment

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

redundant

String serverVersion;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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.prebid.server.version.PrebidVersionProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
Expand All @@ -34,6 +35,7 @@ NextMillenniumConfigurationProperties configurationProperties() {
@Bean
BidderDeps nextMillenniumBidderDeps(NextMillenniumConfigurationProperties nextMillenniumConfigurationProperties,
@NotBlank @Value("${external-url}") String externalUrl,
PrebidVersionProvider prebidVersionProvider,
JacksonMapper mapper) {

return BidderDepsAssembler.<NextMillenniumConfigurationProperties>forBidder(BIDDER_NAME)
Expand All @@ -42,7 +44,8 @@ BidderDeps nextMillenniumBidderDeps(NextMillenniumConfigurationProperties nextMi
.bidderCreator(config -> new NextMillenniumBidder(
config.getEndpoint(),
mapper,
config.getExtraInfo().getNmmFlags())
config.getExtraInfo().getNmmFlags(),
prebidVersionProvider)
).assemble();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.prebid.server.bidder.nextmillennium;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.iab.openrtb.request.App;
import com.iab.openrtb.request.Banner;
import com.iab.openrtb.request.BidRequest;
Expand All @@ -12,7 +11,11 @@
import com.iab.openrtb.response.Bid;
import com.iab.openrtb.response.BidResponse;
import com.iab.openrtb.response.SeatBid;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.prebid.server.VertxTest;
import org.prebid.server.bidder.model.BidderBid;
import org.prebid.server.bidder.model.BidderCall;
Expand All @@ -27,6 +30,7 @@
import org.prebid.server.proto.openrtb.ext.request.ExtStoredRequest;
import org.prebid.server.proto.openrtb.ext.request.nextmillennium.ExtImpNextMillennium;
import org.prebid.server.proto.openrtb.ext.response.BidType;
import org.prebid.server.version.PrebidVersionProvider;

import java.util.Arrays;
import java.util.List;
Expand All @@ -39,17 +43,33 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;

@ExtendWith(MockitoExtension.class)
public class NextMillenniumBidderTest extends VertxTest {

private static final String ENDPOINT_URL = "https://test-url.com/";

private final NextMillenniumBidder target =
new NextMillenniumBidder(ENDPOINT_URL, jacksonMapper, List.of("valueOne", "valueTwo"));
@Mock
private PrebidVersionProvider prebidVersionProvider;

private NextMillenniumBidder target;

@BeforeEach
public void setUp() {
target = new NextMillenniumBidder(
ENDPOINT_URL,
jacksonMapper,
List.of("valueOne", "valueTwo"),
prebidVersionProvider);
}

@Test
public void creationShouldFailOnInvalidEndpointUrl() {
assertThatIllegalArgumentException().isThrownBy(() ->
new NextMillenniumBidder("invalid_url", jacksonMapper, List.of("valueOne", "valueTwo")));
new NextMillenniumBidder(
"invalid_url",
jacksonMapper,
List.of("valueOne", "valueTwo"),
prebidVersionProvider));
}

@Test
Expand Down Expand Up @@ -257,24 +277,6 @@ public void makeHttpRequestsWithInvalidImpsShouldReturnError() {
assertThat(result.getValue()).isEmpty();
}

@Test
public void makeHttpRequestsShouldReturnImpExtNextMillenniumWhenNmmFlagsConfigured() {
// given
final BidRequest bidRequest = givenBidRequest(identity(),
givenImpWithExt(identity(), ExtImpNextMillennium.of("placement1", "group1")));

// when
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);

// then
assertThat(result.getErrors()).isEmpty();
assertThat(result.getValue())
.extracting(HttpRequest::getPayload)
.flatExtracting(BidRequest::getImp)
.extracting(Imp::getExt)
.containsExactly(createImpExt(List.of("valueOne", "valueTwo")));
}

@Test
public void makeBidsShouldReturnBannerBidWhenMTypeIsOne() throws JsonProcessingException {
// given
Expand Down Expand Up @@ -438,19 +440,15 @@ public void makeHttpRequestsImpExtComparison() {

// when
final Result<List<HttpRequest<BidRequest>>> result = target.makeHttpRequests(bidRequest);

// then
assertThat(result.getValue())
.extracting(HttpRequest::getPayload)
.extracting(BidRequest::getImp)
.extracting(imps -> imps.getFirst())
.extracting(Imp::getExt)
.isNotEqualTo(givenImp.getExt())
.extracting(jsonNodes -> mapper.treeToValue(jsonNodes, ExtRequest.class))
.extracting(BidRequest::getExt)
.extracting(ExtRequest::getPrebid)
.extracting(ExtRequestPrebid::getStoredrequest)
.extracting(ExtStoredRequest::getId)
.element(0)
.isEqualTo(placementId);
.containsExactly(placementId);
}

@Test
Expand Down Expand Up @@ -487,7 +485,7 @@ public void makeHttpRequestsImpShouldBeIdenticalExceptExt() {
assertThat(result.getValue())
.extracting(HttpRequest::getPayload)
.extracting(BidRequest::getImp)
.extracting(imps -> imps.getFirst())
.extracting(List::getFirst)
.element(0)
.usingRecursiveComparison()
.ignoringFields("ext")
Expand Down Expand Up @@ -570,13 +568,4 @@ private static Imp givenImpWithExt(UnaryOperator<Imp.ImpBuilder> impCustomizer,
return givenImp(impCustomizer.andThen(imp -> imp.ext(mapper.valueToTree(
ExtPrebid.of(null, extImpNextMillennium))))::apply);
}

private static ObjectNode createImpExt(List<String> values) {
final ObjectNode objectNode = mapper.createObjectNode();
objectNode.set("prebid", mapper.valueToTree(ExtRequestPrebid.builder()
.storedrequest(ExtStoredRequest.of("ggroup1;;")).build()));
objectNode.putObject("nextMillennium")
.set("nmmFlags", mapper.valueToTree(values));
return objectNode;
}
}
4 changes: 3 additions & 1 deletion src/test/java/org/prebid/server/it/NextMillenniumTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ public void openrtb2AuctionShouldRespondWithBidsFromNextMillennium() throws IOEx
// given
WIRE_MOCK_RULE.stubFor(post(urlPathEqualTo("/nextmillennium-exchange"))
.withRequestBody(equalToJson(
jsonFrom("openrtb2/nextmillennium/test-nextmillennium-bid-request.json")))
jsonFrom("openrtb2/nextmillennium/test-nextmillennium-bid-request.json"),
true,
true))
.willReturn(aResponse().withBody(
jsonFrom("openrtb2/nextmillennium/test-nextmillennium-bid-response.json"))));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,18 @@
"imp": [
{
"id": "imp_id",
"secure": 1,
"banner": {
"w": 300,
"h": 250
},
"secure": 1,
"ext": {
"prebid": {
"storedrequest": {
"id": "placement_id"
}
},
"nextMillennium": {
"nmmFlags": [
"1"
]
"bidder": {
"placement_id": "placement_id"
}
}
}
],
"source": {
"tid": "${json-unit.any-string}"
},
"site": {
"domain": "www.example.com",
"page": "http://www.example.com",
Expand All @@ -44,6 +34,9 @@
"cur": [
"USD"
],
"source": {
"tid": "${json-unit.any-string}"
},
"regs": {
"ext": {
"gdpr": 0
Expand All @@ -60,6 +53,11 @@
"datacenter": "local",
"endpoint": "/openrtb2/auction"
}
},
"next_millennium": {
"nmm_flags": [ "1" ],
"nm_version": "v1.0.0",
"server_version": "${json-unit.any-string}"
}
}
}
Loading