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()); + } + } +}