diff --git a/docs/developers/openrtb-model-inheritance.md b/docs/developers/openrtb-model-inheritance.md
new file mode 100644
index 00000000000..91032c3dce1
--- /dev/null
+++ b/docs/developers/openrtb-model-inheritance.md
@@ -0,0 +1,36 @@
+# Extending OpenRTB models
+
+Applications that use Prebid Server Java as a JAR dependency can subclass the models in
+`com.iab.openrtb.request` and `com.iab.openrtb.response` from their own packages. This includes the nested OpenRTB and
+Native Ads objects, so an integration can extend individual parts of a request or response as well as the root object.
+
+This is useful when reusing bidder adapters in an application that has its own request and response representation.
+A subclass can carry local context or implement application-specific behavior while remaining assignable to the concrete
+OpenRTB types accepted by existing APIs. Without inheritance, an unrelated wrapper cannot be passed to those APIs;
+integrations must construct the Prebid model or maintain a modified copy of it.
+
+The value classes retain their private final fields and existing builders. Their all-arguments constructors are protected
+for use by subclasses, and the existing `of(...)` factories remain available. `BrandVersion` retains its existing public
+constructor. The already extensible `Native` class retains its constructors and builder.
+
+For example, a response subclass can initialize the inherited state from an existing response:
+
+```java
+public class ApplicationBidResponse extends BidResponse {
+
+ public ApplicationBidResponse(BidResponse response) {
+ super(response.getId(), response.getSeatbid(), response.getBidid(), response.getCur(),
+ response.getCustomdata(), response.getNbr(), response.getExt());
+ }
+}
+```
+
+Subclassing does not change the behavior of the generated builders: `build()` and `toBuilder().build()` produce the
+declared model type, not the application's subtype. In particular, generated `toBuilder()` methods copy backing fields,
+so overriding getters alone does not implement a lazy or copy-on-write proxy. Builder inheritance and change tracking
+are outside the scope of this extension point. Custom accessors used by equality, such as `Imp.bidFloor()`, may also read
+backing fields directly. Subclasses must account for this when overriding getters or equality.
+
+Applications are responsible for their subclasses' serialization, equality and state ownership. Additional getters can
+become JSON properties, and mutable collections or extension nodes are not made immutable by inheritance. Constructor
+signatures follow the model fields, so subclasses may need to be updated when upgrading the dependency.
diff --git a/src/main/java/com/iab/openrtb/request/App.java b/src/main/java/com/iab/openrtb/request/App.java
index 505bedef9d8..8adec6dc2a0 100644
--- a/src/main/java/com/iab/openrtb/request/App.java
+++ b/src/main/java/com/iab/openrtb/request/App.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtApp;
import java.util.List;
@@ -14,6 +17,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class App {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Asset.java b/src/main/java/com/iab/openrtb/request/Asset.java
index 7be06cd7e03..ebb97976315 100644
--- a/src/main/java/com/iab/openrtb/request/Asset.java
+++ b/src/main/java/com/iab/openrtb/request/Asset.java
@@ -1,11 +1,16 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Asset {
public static final Asset EMPTY = Asset.builder().build();
diff --git a/src/main/java/com/iab/openrtb/request/Audio.java b/src/main/java/com/iab/openrtb/request/Audio.java
index 58901a625b3..298e3772641 100644
--- a/src/main/java/com/iab/openrtb/request/Audio.java
+++ b/src/main/java/com/iab/openrtb/request/Audio.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.math.BigDecimal;
import java.util.List;
@@ -23,6 +26,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Audio {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Banner.java b/src/main/java/com/iab/openrtb/request/Banner.java
index b24584b110b..acfafa5deb3 100644
--- a/src/main/java/com/iab/openrtb/request/Banner.java
+++ b/src/main/java/com/iab/openrtb/request/Banner.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -23,6 +26,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Banner {
/**
diff --git a/src/main/java/com/iab/openrtb/request/BidRequest.java b/src/main/java/com/iab/openrtb/request/BidRequest.java
index 79de78bb242..9136d59a1ec 100644
--- a/src/main/java/com/iab/openrtb/request/BidRequest.java
+++ b/src/main/java/com/iab/openrtb/request/BidRequest.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtRequest;
import java.util.List;
@@ -20,6 +23,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class BidRequest {
/**
diff --git a/src/main/java/com/iab/openrtb/request/BrandVersion.java b/src/main/java/com/iab/openrtb/request/BrandVersion.java
index 6c7931ebc60..9bed8a1280b 100644
--- a/src/main/java/com/iab/openrtb/request/BrandVersion.java
+++ b/src/main/java/com/iab/openrtb/request/BrandVersion.java
@@ -2,6 +2,7 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -12,6 +13,7 @@
* platform or operating system.
*/
@Value
+@NonFinal
public class BrandVersion {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Channel.java b/src/main/java/com/iab/openrtb/request/Channel.java
index 84e0c7d2748..301f04013ed 100644
--- a/src/main/java/com/iab/openrtb/request/Channel.java
+++ b/src/main/java/com/iab/openrtb/request/Channel.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
/**
* This object describes the channel an ad will be displayed on. A
@@ -16,6 +19,8 @@
*/
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Channel {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Content.java b/src/main/java/com/iab/openrtb/request/Content.java
index 486630d8eed..22384842d98 100644
--- a/src/main/java/com/iab/openrtb/request/Content.java
+++ b/src/main/java/com/iab/openrtb/request/Content.java
@@ -2,8 +2,11 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -18,6 +21,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Content {
private static final Content EMPTY = Content.builder().build();
diff --git a/src/main/java/com/iab/openrtb/request/Data.java b/src/main/java/com/iab/openrtb/request/Data.java
index fd48b7100b8..382db7c0365 100644
--- a/src/main/java/com/iab/openrtb/request/Data.java
+++ b/src/main/java/com/iab/openrtb/request/Data.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -16,6 +19,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Data {
/**
diff --git a/src/main/java/com/iab/openrtb/request/DataObject.java b/src/main/java/com/iab/openrtb/request/DataObject.java
index 8484c305c94..25f83871303 100644
--- a/src/main/java/com/iab/openrtb/request/DataObject.java
+++ b/src/main/java/com/iab/openrtb/request/DataObject.java
@@ -1,11 +1,16 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class DataObject {
Integer type;
diff --git a/src/main/java/com/iab/openrtb/request/Deal.java b/src/main/java/com/iab/openrtb/request/Deal.java
index 7c510b002c7..83824a003f8 100644
--- a/src/main/java/com/iab/openrtb/request/Deal.java
+++ b/src/main/java/com/iab/openrtb/request/Deal.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.math.BigDecimal;
import java.util.List;
@@ -15,6 +18,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Deal {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Device.java b/src/main/java/com/iab/openrtb/request/Device.java
index 848e1b01e66..7bc4234e162 100644
--- a/src/main/java/com/iab/openrtb/request/Device.java
+++ b/src/main/java/com/iab/openrtb/request/Device.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtDevice;
import java.math.BigDecimal;
@@ -26,6 +29,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Device {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Dooh.java b/src/main/java/com/iab/openrtb/request/Dooh.java
index b148ed62925..3ba992a8257 100644
--- a/src/main/java/com/iab/openrtb/request/Dooh.java
+++ b/src/main/java/com/iab/openrtb/request/Dooh.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtDooh;
import java.util.List;
@@ -13,6 +16,8 @@
*/
@Value
@Builder(toBuilder = true)
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Dooh {
/**
diff --git a/src/main/java/com/iab/openrtb/request/DurFloor.java b/src/main/java/com/iab/openrtb/request/DurFloor.java
index c03c5f657ee..5f5b96d80c3 100644
--- a/src/main/java/com/iab/openrtb/request/DurFloor.java
+++ b/src/main/java/com/iab/openrtb/request/DurFloor.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.math.BigDecimal;
@@ -11,6 +14,8 @@
*/
@Value
@Builder
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class DurFloor {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Eid.java b/src/main/java/com/iab/openrtb/request/Eid.java
index 04892b57cc6..08da48151b8 100644
--- a/src/main/java/com/iab/openrtb/request/Eid.java
+++ b/src/main/java/com/iab/openrtb/request/Eid.java
@@ -1,13 +1,18 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@Value
@Builder(toBuilder = true)
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Eid {
String source;
diff --git a/src/main/java/com/iab/openrtb/request/EventTracker.java b/src/main/java/com/iab/openrtb/request/EventTracker.java
index f25684cdc23..e0b992bb235 100644
--- a/src/main/java/com/iab/openrtb/request/EventTracker.java
+++ b/src/main/java/com/iab/openrtb/request/EventTracker.java
@@ -1,13 +1,18 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class EventTracker {
Integer event;
diff --git a/src/main/java/com/iab/openrtb/request/Format.java b/src/main/java/com/iab/openrtb/request/Format.java
index 6d719cde4bb..d31e77c1d6e 100644
--- a/src/main/java/com/iab/openrtb/request/Format.java
+++ b/src/main/java/com/iab/openrtb/request/Format.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
/**
* This object represents an allowed size (i.e., height and width combination)
@@ -13,6 +16,8 @@
*/
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Format {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Geo.java b/src/main/java/com/iab/openrtb/request/Geo.java
index 8fc9241a8f9..18157e3b5c8 100644
--- a/src/main/java/com/iab/openrtb/request/Geo.java
+++ b/src/main/java/com/iab/openrtb/request/Geo.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtGeo;
/**
@@ -16,6 +19,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Geo {
public static final Geo EMPTY = Geo.builder().build();
diff --git a/src/main/java/com/iab/openrtb/request/ImageObject.java b/src/main/java/com/iab/openrtb/request/ImageObject.java
index ead65eb4d75..4afdf0aca5a 100644
--- a/src/main/java/com/iab/openrtb/request/ImageObject.java
+++ b/src/main/java/com/iab/openrtb/request/ImageObject.java
@@ -1,13 +1,18 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ImageObject {
Integer type;
diff --git a/src/main/java/com/iab/openrtb/request/Imp.java b/src/main/java/com/iab/openrtb/request/Imp.java
index a24e90d464e..07e5c309cd2 100644
--- a/src/main/java/com/iab/openrtb/request/Imp.java
+++ b/src/main/java/com/iab/openrtb/request/Imp.java
@@ -2,10 +2,13 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.math.BigDecimal;
import java.util.List;
@@ -25,6 +28,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Imp {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Metric.java b/src/main/java/com/iab/openrtb/request/Metric.java
index 6ca53540a3d..97fa9fb61e9 100644
--- a/src/main/java/com/iab/openrtb/request/Metric.java
+++ b/src/main/java/com/iab/openrtb/request/Metric.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
/**
* This object is associated with an impression as an array of metrics.
@@ -13,6 +16,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Metric {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Network.java b/src/main/java/com/iab/openrtb/request/Network.java
index 8d3e3350f92..d87d9450c23 100644
--- a/src/main/java/com/iab/openrtb/request/Network.java
+++ b/src/main/java/com/iab/openrtb/request/Network.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
/**
* This object describes the network an ad will be displayed on.A
@@ -16,6 +19,8 @@
*/
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Network {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Pmp.java b/src/main/java/com/iab/openrtb/request/Pmp.java
index 49a0cc75fdb..c9c076f9dda 100644
--- a/src/main/java/com/iab/openrtb/request/Pmp.java
+++ b/src/main/java/com/iab/openrtb/request/Pmp.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -14,6 +17,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Pmp {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Producer.java b/src/main/java/com/iab/openrtb/request/Producer.java
index 15b781e6015..db6d8ac2bcc 100644
--- a/src/main/java/com/iab/openrtb/request/Producer.java
+++ b/src/main/java/com/iab/openrtb/request/Producer.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -14,6 +17,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Producer {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Publisher.java b/src/main/java/com/iab/openrtb/request/Publisher.java
index 58078e3ecad..1527d4681c9 100644
--- a/src/main/java/com/iab/openrtb/request/Publisher.java
+++ b/src/main/java/com/iab/openrtb/request/Publisher.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtPublisher;
import java.util.List;
@@ -12,6 +15,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Publisher {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Qty.java b/src/main/java/com/iab/openrtb/request/Qty.java
index 64e882de199..141f56b2362 100644
--- a/src/main/java/com/iab/openrtb/request/Qty.java
+++ b/src/main/java/com/iab/openrtb/request/Qty.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.math.BigDecimal;
@@ -14,6 +17,8 @@
*/
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Qty {
/**
diff --git a/src/main/java/com/iab/openrtb/request/RefSettings.java b/src/main/java/com/iab/openrtb/request/RefSettings.java
index cb77f5e22c4..eb99765d770 100644
--- a/src/main/java/com/iab/openrtb/request/RefSettings.java
+++ b/src/main/java/com/iab/openrtb/request/RefSettings.java
@@ -1,11 +1,16 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class RefSettings {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Refresh.java b/src/main/java/com/iab/openrtb/request/Refresh.java
index 48f606bc266..fe9375287ef 100644
--- a/src/main/java/com/iab/openrtb/request/Refresh.java
+++ b/src/main/java/com/iab/openrtb/request/Refresh.java
@@ -1,13 +1,18 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Refresh {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Regs.java b/src/main/java/com/iab/openrtb/request/Regs.java
index ffee5dd3216..4544645e933 100644
--- a/src/main/java/com/iab/openrtb/request/Regs.java
+++ b/src/main/java/com/iab/openrtb/request/Regs.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtRegs;
import java.util.List;
@@ -13,6 +16,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Regs {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Request.java b/src/main/java/com/iab/openrtb/request/Request.java
index 09ddac68d34..0af584f9645 100644
--- a/src/main/java/com/iab/openrtb/request/Request.java
+++ b/src/main/java/com/iab/openrtb/request/Request.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -18,6 +21,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Request {
/** Version in use. **/
diff --git a/src/main/java/com/iab/openrtb/request/Segment.java b/src/main/java/com/iab/openrtb/request/Segment.java
index 3c765bee874..1ae973f414f 100644
--- a/src/main/java/com/iab/openrtb/request/Segment.java
+++ b/src/main/java/com/iab/openrtb/request/Segment.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
/**
* Segment objects are essentially key-value pairs that convey specific units of
@@ -13,6 +16,8 @@
*/
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Segment {
/** ID of the data segment specific to the data provider. */
diff --git a/src/main/java/com/iab/openrtb/request/Site.java b/src/main/java/com/iab/openrtb/request/Site.java
index f71b12beb7b..4b200d65f80 100644
--- a/src/main/java/com/iab/openrtb/request/Site.java
+++ b/src/main/java/com/iab/openrtb/request/Site.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtSite;
import java.util.List;
@@ -14,6 +17,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Site {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Source.java b/src/main/java/com/iab/openrtb/request/Source.java
index 142a55fc7c4..eb3ab0f86af 100644
--- a/src/main/java/com/iab/openrtb/request/Source.java
+++ b/src/main/java/com/iab/openrtb/request/Source.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtSource;
/**
@@ -15,6 +18,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Source {
/**
diff --git a/src/main/java/com/iab/openrtb/request/SupplyChain.java b/src/main/java/com/iab/openrtb/request/SupplyChain.java
index dd3f76685c4..ba00b211a79 100644
--- a/src/main/java/com/iab/openrtb/request/SupplyChain.java
+++ b/src/main/java/com/iab/openrtb/request/SupplyChain.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -13,7 +16,9 @@
* in the direct flow of payment for inventory. Detailed
* implementation examples can be found here.
*/
-@Value(staticConstructor = "of")
+@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class SupplyChain {
/**
@@ -45,4 +50,8 @@ public class SupplyChain {
* Placeholder for advertising-system specific extensions to this object.
*/
ObjectNode ext;
+
+ public static SupplyChain of(Integer complete, List nodes, String ver, ObjectNode ext) {
+ return new SupplyChain(complete, nodes, ver, ext);
+ }
}
diff --git a/src/main/java/com/iab/openrtb/request/SupplyChainNode.java b/src/main/java/com/iab/openrtb/request/SupplyChainNode.java
index ead56a2cdae..3cffd531cb0 100644
--- a/src/main/java/com/iab/openrtb/request/SupplyChainNode.java
+++ b/src/main/java/com/iab/openrtb/request/SupplyChainNode.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Value;
+import lombok.experimental.NonFinal;
/**
* This object is associated with a SupplyChain object as an array of nodes.
@@ -9,7 +12,9 @@
* chain of a bid request. Detailed implementation examples can be found
* here.
*/
-@Value(staticConstructor = "of")
+@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class SupplyChainNode {
/**
@@ -72,4 +77,16 @@ public class SupplyChainNode {
* Placeholder for advertising-system specific extensions to this object.
*/
ObjectNode ext;
+
+ public static SupplyChainNode of(
+ String asi,
+ String sid,
+ String rid,
+ String name,
+ String domain,
+ Integer hp,
+ ObjectNode ext) {
+
+ return new SupplyChainNode(asi, sid, rid, name, domain, hp, ext);
+ }
}
diff --git a/src/main/java/com/iab/openrtb/request/TitleObject.java b/src/main/java/com/iab/openrtb/request/TitleObject.java
index ae09d8d7caa..b0dc9fcab14 100644
--- a/src/main/java/com/iab/openrtb/request/TitleObject.java
+++ b/src/main/java/com/iab/openrtb/request/TitleObject.java
@@ -1,11 +1,16 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class TitleObject {
Integer len;
diff --git a/src/main/java/com/iab/openrtb/request/Uid.java b/src/main/java/com/iab/openrtb/request/Uid.java
index c90e563b9d5..f282daf2f8f 100644
--- a/src/main/java/com/iab/openrtb/request/Uid.java
+++ b/src/main/java/com/iab/openrtb/request/Uid.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
/**
* This object contains a single user identifier provided as part of
@@ -11,6 +14,8 @@
*/
@Value
@Builder(toBuilder = true)
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Uid {
/**
diff --git a/src/main/java/com/iab/openrtb/request/User.java b/src/main/java/com/iab/openrtb/request/User.java
index c55f6b7e1af..f76515abaaa 100644
--- a/src/main/java/com/iab/openrtb/request/User.java
+++ b/src/main/java/com/iab/openrtb/request/User.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.request;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.request.ExtUser;
import java.util.List;
@@ -15,6 +18,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
public static final User EMPTY = User.builder().build();
diff --git a/src/main/java/com/iab/openrtb/request/UserAgent.java b/src/main/java/com/iab/openrtb/request/UserAgent.java
index a14270ca71e..acf563ac546 100644
--- a/src/main/java/com/iab/openrtb/request/UserAgent.java
+++ b/src/main/java/com/iab/openrtb/request/UserAgent.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -16,6 +19,8 @@
*/
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class UserAgent {
/**
diff --git a/src/main/java/com/iab/openrtb/request/Video.java b/src/main/java/com/iab/openrtb/request/Video.java
index 369d576a3ac..3ac344b0d51 100644
--- a/src/main/java/com/iab/openrtb/request/Video.java
+++ b/src/main/java/com/iab/openrtb/request/Video.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.math.BigDecimal;
import java.util.List;
@@ -23,6 +26,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Video {
/**
diff --git a/src/main/java/com/iab/openrtb/request/VideoObject.java b/src/main/java/com/iab/openrtb/request/VideoObject.java
index 580554a81c2..7c9e0cafc8c 100644
--- a/src/main/java/com/iab/openrtb/request/VideoObject.java
+++ b/src/main/java/com/iab/openrtb/request/VideoObject.java
@@ -1,13 +1,18 @@
package com.iab.openrtb.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class VideoObject {
List mimes;
diff --git a/src/main/java/com/iab/openrtb/response/Asset.java b/src/main/java/com/iab/openrtb/response/Asset.java
index cbcdb8c6590..19bae3e790a 100644
--- a/src/main/java/com/iab/openrtb/response/Asset.java
+++ b/src/main/java/com/iab/openrtb/response/Asset.java
@@ -2,11 +2,16 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Asset {
Integer id;
diff --git a/src/main/java/com/iab/openrtb/response/Bid.java b/src/main/java/com/iab/openrtb/response/Bid.java
index 6210db2aedf..7dd80a09e64 100644
--- a/src/main/java/com/iab/openrtb/response/Bid.java
+++ b/src/main/java/com/iab/openrtb/response/Bid.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.response;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.math.BigDecimal;
import java.util.List;
@@ -15,6 +18,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Bid {
/**
diff --git a/src/main/java/com/iab/openrtb/response/BidResponse.java b/src/main/java/com/iab/openrtb/response/BidResponse.java
index 12a841bc7da..7cc8e40798e 100644
--- a/src/main/java/com/iab/openrtb/response/BidResponse.java
+++ b/src/main/java/com/iab/openrtb/response/BidResponse.java
@@ -1,7 +1,10 @@
package com.iab.openrtb.response;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import org.prebid.server.proto.openrtb.ext.response.ExtBidResponse;
import java.util.List;
@@ -21,6 +24,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class BidResponse {
/**
diff --git a/src/main/java/com/iab/openrtb/response/DataObject.java b/src/main/java/com/iab/openrtb/response/DataObject.java
index e403bb883ba..10e7280c8f7 100644
--- a/src/main/java/com/iab/openrtb/response/DataObject.java
+++ b/src/main/java/com/iab/openrtb/response/DataObject.java
@@ -1,11 +1,14 @@
package com.iab.openrtb.response;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@Builder
@Data
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class DataObject {
Integer type;
diff --git a/src/main/java/com/iab/openrtb/response/EventTracker.java b/src/main/java/com/iab/openrtb/response/EventTracker.java
index 803177e7536..34d92657ec7 100644
--- a/src/main/java/com/iab/openrtb/response/EventTracker.java
+++ b/src/main/java/com/iab/openrtb/response/EventTracker.java
@@ -1,13 +1,18 @@
package com.iab.openrtb.response;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.Map;
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class EventTracker {
/**
diff --git a/src/main/java/com/iab/openrtb/response/ImageObject.java b/src/main/java/com/iab/openrtb/response/ImageObject.java
index 1c4fdf9ef00..e6e968d13cd 100644
--- a/src/main/java/com/iab/openrtb/response/ImageObject.java
+++ b/src/main/java/com/iab/openrtb/response/ImageObject.java
@@ -1,11 +1,14 @@
package com.iab.openrtb.response;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@Builder
@Data
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ImageObject {
Integer type;
diff --git a/src/main/java/com/iab/openrtb/response/Link.java b/src/main/java/com/iab/openrtb/response/Link.java
index 57f740f00e9..520c57ece26 100644
--- a/src/main/java/com/iab/openrtb/response/Link.java
+++ b/src/main/java/com/iab/openrtb/response/Link.java
@@ -1,14 +1,19 @@
package com.iab.openrtb.response;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
/**
* Used for ‘call to action’ assets, or other links from the Native ad.
*/
-@Value(staticConstructor = "of")
+@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Link {
/**
@@ -27,4 +32,8 @@ public class Link {
String fallback;
ObjectNode ext;
+
+ public static Link of(String url, List clicktrackers, String fallback, ObjectNode ext) {
+ return new Link(url, clicktrackers, fallback, ext);
+ }
}
diff --git a/src/main/java/com/iab/openrtb/response/Response.java b/src/main/java/com/iab/openrtb/response/Response.java
index d1a3df2bcd1..fbf38de82c3 100644
--- a/src/main/java/com/iab/openrtb/response/Response.java
+++ b/src/main/java/com/iab/openrtb/response/Response.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.response;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -15,6 +18,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class Response {
/**
diff --git a/src/main/java/com/iab/openrtb/response/SeatBid.java b/src/main/java/com/iab/openrtb/response/SeatBid.java
index c4f3cd3d6eb..acc8cfd1667 100644
--- a/src/main/java/com/iab/openrtb/response/SeatBid.java
+++ b/src/main/java/com/iab/openrtb/response/SeatBid.java
@@ -1,8 +1,11 @@
package com.iab.openrtb.response;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
import java.util.List;
@@ -16,6 +19,8 @@
*/
@Builder(toBuilder = true)
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class SeatBid {
/**
diff --git a/src/main/java/com/iab/openrtb/response/TitleObject.java b/src/main/java/com/iab/openrtb/response/TitleObject.java
index ed07e9ff010..262aa749ecc 100644
--- a/src/main/java/com/iab/openrtb/response/TitleObject.java
+++ b/src/main/java/com/iab/openrtb/response/TitleObject.java
@@ -1,11 +1,16 @@
package com.iab.openrtb.response;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class TitleObject {
String text;
diff --git a/src/main/java/com/iab/openrtb/response/VideoObject.java b/src/main/java/com/iab/openrtb/response/VideoObject.java
index b74079d696d..e07512a6249 100644
--- a/src/main/java/com/iab/openrtb/response/VideoObject.java
+++ b/src/main/java/com/iab/openrtb/response/VideoObject.java
@@ -1,13 +1,18 @@
package com.iab.openrtb.response;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
+import lombok.experimental.NonFinal;
/**
* Corresponds to the Video Object in the request, yet containing a value of a conforming VAST tag as a value.
*/
@Builder
@Value
+@NonFinal
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class VideoObject {
/**
diff --git a/src/test/java/org/prebid/server/json/OpenRtbModelInheritanceTest.java b/src/test/java/org/prebid/server/json/OpenRtbModelInheritanceTest.java
new file mode 100644
index 00000000000..adbc1c3ec02
--- /dev/null
+++ b/src/test/java/org/prebid/server/json/OpenRtbModelInheritanceTest.java
@@ -0,0 +1,215 @@
+package org.prebid.server.json;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.iab.openrtb.request.BidRequest;
+import com.iab.openrtb.request.BrandVersion;
+import com.iab.openrtb.request.Imp;
+import com.iab.openrtb.request.SupplyChain;
+import com.iab.openrtb.request.SupplyChainNode;
+import com.iab.openrtb.response.Bid;
+import com.iab.openrtb.response.BidResponse;
+import com.iab.openrtb.response.DataObject;
+import com.iab.openrtb.response.ImageObject;
+import com.iab.openrtb.response.Link;
+import com.iab.openrtb.response.SeatBid;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OpenRtbModelInheritanceTest {
+
+ private final ObjectMapper mapper = ObjectMapperProvider.mapper();
+
+ @Test
+ void requestSubclassShouldRetainFieldsAndJsonRepresentation() throws Exception {
+ // given
+ final BidRequest source = BidRequest.builder()
+ .id("request-id")
+ .tmax(100L)
+ .imp(Collections.singletonList(Imp.builder().id("imp-id").build()))
+ .build();
+
+ // when
+ final BidRequest result = new ApplicationBidRequest(source);
+
+ // then
+ assertThat(result).isEqualTo(source);
+ assertThat(source).isEqualTo(result);
+ assertThat(result.hashCode()).isEqualTo(source.hashCode());
+ assertThat(result.getImp()).isSameAs(source.getImp());
+ assertThat(mapper.readTree(mapper.writeValueAsString(result)))
+ .isEqualTo(mapper.readTree(mapper.writeValueAsString(source)));
+ assertThat(mapper.readValue(mapper.writeValueAsString(result), BidRequest.class)).isEqualTo(source);
+ assertThat(result.toBuilder().tmax(50L).build()).isEqualTo(source.toBuilder().tmax(50L).build());
+ assertThat(result.getTmax()).isEqualTo(100L);
+ }
+
+ @Test
+ void responseSubclassShouldRetainFieldsAndJsonRepresentation() throws Exception {
+ // given
+ final BidResponse source = BidResponse.builder()
+ .id("request-id")
+ .cur("USD")
+ .seatbid(Collections.singletonList(SeatBid.builder()
+ .bid(Collections.singletonList(Bid.builder().id("bid-id").impid("imp-id").build()))
+ .build()))
+ .build();
+
+ // when
+ final BidResponse result = new ApplicationBidResponse(source);
+
+ // then
+ assertThat(result).isEqualTo(source);
+ assertThat(source).isEqualTo(result);
+ assertThat(result.hashCode()).isEqualTo(source.hashCode());
+ assertThat(result.getSeatbid()).isSameAs(source.getSeatbid());
+ assertThat(mapper.readTree(mapper.writeValueAsString(result)))
+ .isEqualTo(mapper.readTree(mapper.writeValueAsString(source)));
+ assertThat(mapper.readValue(mapper.writeValueAsString(result), BidResponse.class)).isEqualTo(source);
+ assertThat(result.toBuilder().cur("EUR").build()).isEqualTo(source.toBuilder().cur("EUR").build());
+ assertThat(result.getCur()).isEqualTo("USD");
+ }
+
+ @Test
+ void supplyChainFactoryShouldPreserveFieldsAndJsonRoundTrip() throws Exception {
+ // given
+ final SupplyChainNode node = SupplyChainNode.of(
+ "ssp.test", "seller-id", "request-id", "seller", "seller.test", 1, mapper.createObjectNode());
+
+ // when
+ final SupplyChain result = SupplyChain.of(1, Collections.singletonList(node), "1.0", mapper.createObjectNode());
+
+ // then
+ assertThat(result.getComplete()).isEqualTo(1);
+ assertThat(result.getNodes()).containsExactly(node);
+ assertThat(result.getVer()).isEqualTo("1.0");
+ assertThat(result.getExt()).isEqualTo(mapper.createObjectNode());
+ assertThat(node.getAsi()).isEqualTo("ssp.test");
+ assertThat(node.getSid()).isEqualTo("seller-id");
+ assertThat(node.getRid()).isEqualTo("request-id");
+ assertThat(node.getName()).isEqualTo("seller");
+ assertThat(node.getDomain()).isEqualTo("seller.test");
+ assertThat(node.getHp()).isEqualTo(1);
+ assertThat(node.getExt()).isEqualTo(mapper.createObjectNode());
+ assertThat(mapper.readValue(mapper.writeValueAsString(result), SupplyChain.class)).isEqualTo(result);
+ }
+
+ @Test
+ void linkSubclassShouldBeCompatibleWithFactoryAndJsonRoundTrip() throws Exception {
+ // given
+ final Link source = Link.of("https://landing.test", Collections.singletonList("https://tracker.test"),
+ "https://fallback.test", mapper.createObjectNode());
+
+ // when
+ final Link result = new Link(
+ source.getUrl(), source.getClicktrackers(), source.getFallback(), source.getExt()) {
+ };
+
+ // then
+ assertThat(result.getUrl()).isEqualTo("https://landing.test");
+ assertThat(result.getClicktrackers()).containsExactly("https://tracker.test");
+ assertThat(result.getFallback()).isEqualTo("https://fallback.test");
+ assertThat(result.getExt()).isEqualTo(mapper.createObjectNode());
+ assertThat(result).isEqualTo(source);
+ assertThat(source).isEqualTo(result);
+ assertThat(mapper.readValue(mapper.writeValueAsString(result), Link.class)).isEqualTo(source);
+ }
+
+ @Test
+ void brandVersionShouldRetainPublicConstructor() throws Exception {
+ // when
+ final BrandVersion result = new BrandVersion("browser", Collections.singletonList("1"), null);
+
+ // then
+ assertThat(result.getBrand()).isEqualTo("browser");
+ assertThat(result.getVersion()).containsExactly("1");
+ assertThat(mapper.readValue(mapper.writeValueAsString(result), BrandVersion.class)).isEqualTo(result);
+ }
+
+ @Test
+ void dataObjectSubclassShouldBeCompatibleWithBuilderAndJsonRoundTrip() throws Exception {
+ // given
+ final DataObject source = DataObject.builder()
+ .type(1).len(5).value("value").ext(mapper.createObjectNode()).build();
+
+ // when
+ final DataObject result = new DataObject(
+ source.getType(), source.getLen(), source.getValue(), source.getExt()) {
+ };
+
+ // then
+ assertThat(result).isEqualTo(source);
+ assertThat(source).isEqualTo(result);
+ assertThat(mapper.readValue(mapper.writeValueAsString(result), DataObject.class)).isEqualTo(source);
+ result.setValue("updated");
+ assertThat(result.getValue()).isEqualTo("updated");
+ assertThat(source.getValue()).isEqualTo("value");
+ }
+
+ @Test
+ void imageObjectSubclassShouldBeCompatibleWithBuilderAndJsonRoundTrip() throws Exception {
+ // given
+ final ImageObject source = ImageObject.builder()
+ .type(1).url("https://image.test").w(100).h(50).ext(mapper.createObjectNode()).build();
+
+ // when
+ final ImageObject result = new ImageObject(
+ source.getType(), source.getUrl(), source.getW(), source.getH(), source.getExt()) {
+ };
+
+ // then
+ assertThat(result).isEqualTo(source);
+ assertThat(source).isEqualTo(result);
+ assertThat(mapper.readValue(mapper.writeValueAsString(result), ImageObject.class)).isEqualTo(source);
+ result.setW(200);
+ assertThat(result.getW()).isEqualTo(200);
+ assertThat(source.getW()).isEqualTo(100);
+ }
+
+ private static class ApplicationBidRequest extends BidRequest {
+
+ private ApplicationBidRequest(BidRequest source) {
+ super(
+ source.getId(),
+ source.getImp(),
+ source.getSite(),
+ source.getApp(),
+ source.getDooh(),
+ source.getDevice(),
+ source.getUser(),
+ source.getTest(),
+ source.getAt(),
+ source.getTmax(),
+ source.getWseat(),
+ source.getBseat(),
+ source.getAllimps(),
+ source.getCur(),
+ source.getWlang(),
+ source.getWlangb(),
+ source.getAcat(),
+ source.getBcat(),
+ source.getCattax(),
+ source.getBadv(),
+ source.getBapp(),
+ source.getSource(),
+ source.getRegs(),
+ source.getExt());
+ }
+ }
+
+ private static class ApplicationBidResponse extends BidResponse {
+
+ private ApplicationBidResponse(BidResponse source) {
+ super(
+ source.getId(),
+ source.getSeatbid(),
+ source.getBidid(),
+ source.getCur(),
+ source.getCustomdata(),
+ source.getNbr(),
+ source.getExt());
+ }
+ }
+}