diff --git a/README.md b/README.md index 7c2d150..d35484d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # openrtb [![GoDoc](https://godoc.org/github.com/mxmCherry/openrtb?status.svg)](https://godoc.org/github.com/mxmCherry/openrtb) [![Build Status](https://travis-ci.org/mxmCherry/openrtb.svg?branch=master)](https://travis-ci.org/mxmCherry/openrtb) -[OpenRTB](//www.iab.com/guidelines/real-time-bidding-rtb-project/) [v2.5](//www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf) types for Go programming language (Golang) +[OpenRTB](https://www.iab.com/guidelines/real-time-bidding-rtb-project/) [v2.5](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf) types for Go programming language (Golang) + +Also includes [OpenRTB](https://www.iab.com/guidelines/real-time-bidding-rtb-project/) [Dynamic Native Ads API +Specification Version 1.1](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-Native-Ads-Specification-1-1_2016.pdf) types: +- [4 Native Ad Request Markup Details](native/request/) +- [5 Native Ad Response Markup Details](native/response/) # Using @@ -12,7 +17,7 @@ go get -u "github.com/mxmCherry/openrtb" import "github.com/mxmCherry/openrtb" ``` -This repo follows [semver](http://semver.org/) - see [releases](//github.com/mxmCherry/openrtb/releases). +This repo follows [semver](http://semver.org/) - see [releases](https://github.com/mxmCherry/openrtb/releases). Master always contains latest code, so better use some package manager to vendor specific version. # Guidelines @@ -25,7 +30,7 @@ Master always contains latest code, so better use some package manager to vendor ## Types - Key types should be chosen according to OpenRTB specification (attribute types) - Numeric types: - - `int64` - time, duration, unbound enums (like `BidRequest.at` - exchange-specific auctions types are > 500) + - `int64` - time, duration, length, unbound enums (like `BidRequest.at` - exchange-specific auctions types are > 500) - `int8` - short enums (with values <= 127), boolean-like attributes (like `BidRequest.test`) - `uint64` - width, height, bitrate etc. (unbound positive numbers) - `float64` - coordinates, prices etc. diff --git a/bid_request_test.go b/bid_request_test.go new file mode 100644 index 0000000..c22b5b1 --- /dev/null +++ b/bid_request_test.go @@ -0,0 +1,56 @@ +package openrtb_test + +import ( + "encoding/json" + "io/ioutil" + "path/filepath" + + . "github.com/mxmCherry/openrtb" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" +) + +var _ = Describe("BidRequest", func() { + DescribeTable( + "Marshaling", + + func(filename string, subject interface{}) { + expected, err := ioutil.ReadFile(filepath.Join("testdata", filename)) + Expect(err).NotTo(HaveOccurred()) + + Expect(json.Unmarshal(expected, subject)).To(Succeed()) + + actual, err := json.Marshal(subject) + Expect(err).NotTo(HaveOccurred()) + + Expect(actual).To(MatchJSON(expected)) + }, + + Entry( + "Simple Banner", + "bid-request/simple-banner.json", + new(BidRequest)), + Entry( + "Expandable Creative", + "bid-request/expandable-creative.json", + new(BidRequest)), + Entry( + "Mobile", + "bid-request/mobile.json", + new(BidRequest)), + Entry( + "Video", + "bid-request/video.json", + new(BidRequest)), + Entry( + "PMP with Direct Deal", + "bid-request/pmp-with-direct-deal.json", + new(BidRequest)), + Entry( + "Native Ad", + "bid-request/native-ad.json", + new(BidRequest)), + ) +}) diff --git a/bid_response_test.go b/bid_response_test.go new file mode 100644 index 0000000..e2bc236 --- /dev/null +++ b/bid_response_test.go @@ -0,0 +1,48 @@ +package openrtb_test + +import ( + "encoding/json" + "io/ioutil" + "path/filepath" + + . "github.com/mxmCherry/openrtb" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" +) + +var _ = Describe("BidResponse", func() { + DescribeTable( + "Marshaling", + + func(filename string, subject interface{}) { + expected, err := ioutil.ReadFile(filepath.Join("testdata", filename)) + Expect(err).NotTo(HaveOccurred()) + + Expect(json.Unmarshal(expected, subject)).To(Succeed()) + + actual, err := json.Marshal(subject) + Expect(err).NotTo(HaveOccurred()) + + Expect(actual).To(MatchJSON(expected)) + }, + + Entry( + "Ad Served on Win Notice", + "bid-response/ad-served-on-win-notice.json", + new(BidResponse)), + Entry( + "VAST XML Document Returned Inline", + "bid-response/vast-xml-document-returned-inline.json", + new(BidResponse)), + Entry( + "Direct Deal Ad Served on Win Notice", + "bid-response/direct-deal-ad-served-on-win-notice.json", + new(BidResponse)), + Entry( + "Native Markup Returned Inline", + "bid-response/native-markup-returned-inline.json", + new(BidResponse)), + ) +}) diff --git a/native/README.md b/native/README.md new file mode 100644 index 0000000..3abb39f --- /dev/null +++ b/native/README.md @@ -0,0 +1,4 @@ +# native + +[Go](https://golang.org/) implementation of [OpenRTB](https://www.iab.com/guidelines/real-time-bidding-rtb-project/) [Dynamic Native Ads API +Specification Version 1.1](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-Native-Ads-Specification-1-1_2016.pdf) types. diff --git a/native/request/README.md b/native/request/README.md new file mode 100644 index 0000000..bd1dd1a --- /dev/null +++ b/native/request/README.md @@ -0,0 +1,4 @@ +# native/request [![GoDoc](https://godoc.org/github.com/mxmCherry/openrtb/native/request?status.svg)](https://godoc.org/github.com/mxmCherry/openrtb/native/request) + +[Go](https://golang.org/) implementation of [OpenRTB](https://www.iab.com/guidelines/real-time-bidding-rtb-project/) [Dynamic Native Ads API +Specification Version 1.1](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-Native-Ads-Specification-1-1_2016.pdf) section 4 Native Ad Request Markup Details types. diff --git a/native/request/ad_unit.go b/native/request/ad_unit.go new file mode 100644 index 0000000..7a90e8e --- /dev/null +++ b/native/request/ad_unit.go @@ -0,0 +1,22 @@ +package request + +// 7.2 Native Ad Unit IDs - To Be Deprecated +// +// Ad Unit ID is to be deprecated in a future version and is not suggested for new implementations. +// +// Below is a list of the core ad unit ids described by IAB here http://www.iab.net/media/file/IABNativeAdvertisingPlaybook120413.pdf +// +// In feed unit is essentially a layout, it has been removed from the list. The in feed units can be identified via the layout parameter on the request. +// +// An implementing exchange may not support all asset variants or introduce new ones unique to that system. +type AdUnit int64 + +const ( + AdUnitPaidSearch AdUnit = 1 // Paid Search Units + AdUnitRecommendationWidget AdUnit = 2 // Recommendation Widgets + AdUnitPromotedListing AdUnit = 3 // Promoted Listings + AdUnitInAd AdUnit = 4 // In-Ad (IAB Standard) with Native Element Units + AdUnitCustom AdUnit = 5 // Custom /”Can’t Be Contained” + + // 500+ Reserved for Exchange specific formats. +) diff --git a/native/request/asset.go b/native/request/asset.go new file mode 100644 index 0000000..47a9a6d --- /dev/null +++ b/native/request/asset.go @@ -0,0 +1,97 @@ +package request + +// 4.2 Asset Object +// +// The main container object for each asset requested or supported by Exchange on behalf of the rendering client. +// Any object that is required is to be flagged as such. +// Only one of the {title,img,video,data} objects should be present in each object. +// All others should be null/absent. +// The id is to be unique within the AssetObject array so that the response can be aligned. +// +// To be more explicit, it is the ID of each asset object that maps the response to the request. +// So if a request for a title object is sent with id 1, then the response containing the title should have an id of 1. +// +// New in version 1.1 of the spec, there are recommended sizes/lengths/etc with some of the asset types. +// The goal for asset requirements standardization is to facilitate adoption of native by DSPs by limiting the diverse types/sizes/requirements of assets they must have available to purchase a native ad impression. +// While great diversity may exist in publishers, advertisers/DSPs can not be expected to provide infinite headline lengths, thumbnail aspect ratios, etc. +// While we have not gone as far as creating a single standard, we've honed in on a few options that cover the most common cases. +// SSPs can deviate from these standards, but should understand they may limit applicable DSP demand by doing so. +// DSPs should feel confident that if they support these standards they'll be able to access most native inventory. +type Asset struct { + // Field: + // id + // Scope: + // required + // Type: + // int + // Description: + // Unique asset ID, assigned by exchange. + // Typically a counter for the array. + ID int64 `json:"id"` + + // Field: + // required + // Scope: + // optional + // Type: + // int + // Default: + // 0 + // Description: + // Set to 1 if asset is required (exchange will not accept a bid without it) + Required int8 `json:"required,omitempty"` + + // Field: + // title + // Scope: + // recommended (each asset object may contain only one of title, img, data or video) + // Type: + // object + // Description: + // Title object for title assets. + // See TitleObject definition. + Title *Title `json:"title,omitempty"` + + // Field: + // img + // Scope: + // recommended (each asset object may contain only one of title, img, data or video) + // Type: + // object + // Description: + // Image object for image assets. + // See ImageObject definition. + Img *Image `json:"img,omitempty"` + + // Field: + // video + // Scope: + // optional (each asset object may contain only one of title, img, data or video) + // Type: + // object - Video object for video assets. + // See the Video request object definition. + // Note that in-stream (ie preroll, etc) video ads are not part of Native. + // Native ads may contain a video as the ad creative itself. + Video *Video `json:"video,omitempty"` + + // Field: + // data + // Scope: + // recommended (each asset object may contain only one of title, img, data or video) + // Type: + // object + // Description: + // Data object for brand name, description, ratings, prices etc. + // See DataObject definition. + Data *Data `json:"data,omitempty"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/request/context_sub_type.go b/native/request/context_sub_type.go new file mode 100644 index 0000000..bc698b8 --- /dev/null +++ b/native/request/context_sub_type.go @@ -0,0 +1,26 @@ +package request + +// 7.4 Context Sub Type IDs +// +// Next-level context in which the ad appears. +// Again this reflects the primary context, and does not imply no presence of other elements. +// For example, an article is likely to contain images but is still first and foremost an article. +// SubType should only be combined with the primary context type as indicated (ie for a context type of 1, only context subtypes that start with 1 are valid). +type ContextSubType int64 + +const ( + ContextSubTypeGeneral ContextSubType = 10 // General or mixed content. + ContextSubTypeArticle ContextSubType = 11 // Primarily article content (which of course could include images, etc as part of the article) + ContextSubTypeVideo ContextSubType = 12 // Primarily video content + ContextSubTypeAudio ContextSubType = 13 // Primarily audio content + ContextSubTypeImage ContextSubType = 14 // Primarily image content + ContextSubTypeUserGenerated ContextSubType = 15 // User-generated content - forums, comments, etc + ContextSubTypeSocial ContextSubType = 20 // General social content such as a general social network + ContextSubTypeEmail ContextSubType = 21 // Primarily email content + ContextSubTypeChat ContextSubType = 22 // Primarily chat/IM content + ContextSubTypeSelling ContextSubType = 30 // Content focused on selling products, whether digital or physical + ContextSubTypeAppStore ContextSubType = 31 // Application store/marketplace + ContextSubTypeProductReview ContextSubType = 32 // Product reviews site primarily (which may sell product secondarily) + + // 500+ To be defined by the exchange +) diff --git a/native/request/context_type.go b/native/request/context_type.go new file mode 100644 index 0000000..2956984 --- /dev/null +++ b/native/request/context_type.go @@ -0,0 +1,16 @@ +package request + +// 7.3 Context Type IDs +// +// The context in which the ad appears - what type of content is surrounding the ad on the page at a high level. +// This maps directly to the new Deep Dive on In-Feed Ad Units. +// This denotes the primary context, but does not imply other content may not exist on the page - for example it's expected that most content platforms have some social components, etc. +type ContextType int64 + +const ( + ContextTypeContent ContextType = 1 // Content-centric context such as newsfeed, article, image gallery, video gallery, or similar. + ContextTypeSocial ContextType = 2 // Social-centric context such as social network feed, email, chat, or similar. + ContextTypeProduct ContextType = 3 // Product context such as product listings, details, recommendations, reviews, or similar. + + // 500+ To be defined by the exchange. +) diff --git a/native/request/data.go b/native/request/data.go new file mode 100644 index 0000000..3a06f89 --- /dev/null +++ b/native/request/data.go @@ -0,0 +1,41 @@ +package request + +// 4.6 Data Object +// +// The Data Object is to be used for all non-core elements of the native unit such as Brand Name, Ratings, Review Count, Stars, Download count, descriptions etc. +// It is also generic for future native elements not contemplated at the time of the writing of this document. +// In some cases, additional recommendations are also included in the Data Asset Types table. +type Data struct { + + // Field: + // type + // Scope: + // required + // Type: + // integer + // Description: + // Type ID of the element supported by the publisher. + // The publisher can display this information in an appropriate format. + // See Data Asset Types table for commonly used examples. + Type DataAssetType `json:"type"` + + // Field: + // len + // Scope: + // optional + // Type: + // integer + // Description: + // Maximum length of the text in the element’s response. + Len int64 `json:"len,omitempty"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/request/data_asset_type.go b/native/request/data_asset_type.go new file mode 100644 index 0000000..a13eb19 --- /dev/null +++ b/native/request/data_asset_type.go @@ -0,0 +1,154 @@ +package request + +// 7.6 Data Asset Types +// +// Below is a list of common asset element types of native advertising at the time of writing this spec. +// This list is non-exhaustive and intended to be extended by the buyers and sellers as the format evolves. +// +// An implementing exchange may not support all asset variants or introduce new ones unique to that system. +type DataAssetType int64 + +const ( + // Type ID: + // 1 + // Name: + // sponsored + // Description: + // Sponsored By message where response should contain the brand name of the sponsor. + // Format: + // text + // Recommendations: + // Required. Max 25 or longer + DataAssetTypeSponsored DataAssetType = 1 + + // Type ID: + // 2 + // Name: + // desc + // Description: + // Descriptive text associated with the product or service being advertised. + // Longer length of text in response may be truncated or ellipsed by th exchange. + // Format: + // text + // Recommendations: + // Recommended. Max 140 or longer. + DataAssetTypeDesc DataAssetType = 2 + + // Type ID: + // 3 + // Name: + // rating + // Description: + // Rating of the product being offered to the user. + // For example an app’s rating in an app store from 0-5. + // Format: + // number formatted as string + // Recommendations: + // Optional. 0-5 integer formatted as string. + DataAssetTypeRating DataAssetType = 3 + + // Type ID: + // 4 + // Name: + // likes + // Description: + // Number of social ratings or “likes” of the product being offered to the user. + // Format: + // number formatted as string + DataAssetTypeLikes DataAssetType = 4 + + // Type ID: + // 5 + // Name: + // downloads + // Description: + // Number downloads/installs of this product + // Format: + // number formatted as string + DataAssetTypeDownloads DataAssetType = 5 + + // Type ID: + // 6 + // Name: + // price + // Description: + // Price for product / app / in-app purchase. + // Value should include currency symbol in localised format. + // Format: + // number formatted as string + DataAssetTypePrice DataAssetType = 6 + + // Type ID: + // 7 + // Name: + // saleprice + // Description: + // Sale price that can be used together with price to indicate a discounted price compared to a regular price. + // Value should include currency symbol in localised format. + // Format: + // number formatted as string + DataAssetTypeSalePrice DataAssetType = 7 + + // Type ID: + // 8 + // Name: + // phone + // Description: + // Phone number formatted + // Format: + // string + DataAssetTypePhone DataAssetType = 8 + + // Type ID: + // 9 + // Name: + // address + // Description: + // Address + // Format: + // text + DataAssetTypeAddress DataAssetType = 9 + + // Type ID: + // 10 + // Name: + // desc2 + // Description: + // Additional descriptive text associated with the product or service being advertised + // Format: + // text + DataAssetTypeDesc2 DataAssetType = 10 + + // Type ID: + // 11 + // Name: + // displayurl + // Description: + // Display URL for the text ad. + // To be used when sponsoring entity doesn’t own the content. + // IE sponsored by BRAND on SITE (where SITE is transmitted in this field). + // Format: + // text + DataAssetTypeDispayURL DataAssetType = 11 + + // Type ID: + // 12 + // Name: + // ctatext + // Dewscription: + // CTA description - descriptive text describing a ‘call to action’ button for the destination URL. + // Format: + // text + // Recommendations: + // Optional. Max 15 or longer. + DataAssetTypeCTAText DataAssetType = 12 + + // Type ID: + // 500+ + // Name: + // XXX + // Description: + // Reserved for Exchange specific usage numbered above 500 + // Format: + // Unknown +) diff --git a/native/request/image.go b/native/request/image.go new file mode 100644 index 0000000..460064c --- /dev/null +++ b/native/request/image.go @@ -0,0 +1,92 @@ +package request + +// 4.4 Image Object +// +// The Image object to be used for all image elements of the Native ad such as Icons, Main Image, etc. +// Recommended sizes and aspect ratios are included in the Image Asset Types section. +type Image struct { + + // Field: + // type + // Scope: + // optional + // Type: + // integer + // Description: + // Type ID of the image element supported by the publisher. + // The publisher can display this information in an appropriate format. + // See Table Image Asset Types. + Type ImageAssetType `json:"type,omitempty"` + + // Field: + // w + // Scope: + // optional + // Type: + // integer + // Description: + // Width of the image in pixels. + W uint64 `json:"w,omitempty"` + + // Field: + // wmin + // Scope: + // recommended + // Type: + // integer + // Description: + // The minimum requested width of the image in pixels. + // This option should be used for any rescaling of images by the client. + // Either w or wmin should be transmitted. + // If only w is included, it should be considered an exact requirement. + WMin uint64 `json:"wmin,omitempty"` + + // Field: + // h + // Scope: + // optional + // Type: + // integer + // Description: + // Height of the image in pixels. + H uint64 `json:"h,omitempty"` + + // Field: + // hmin + // Scope: + // recommended + // Type: + // integer + // Description: + // The minimum requested height of the image in pixels. + // This option should be used for any rescaling of images by the client. + // Either h or hmin should be transmitted. + // If only h is included, it should be considered an exact requirement. + HMin uint64 `json:"hmin,omitempty"` + + // Field: + // mimes + // Scope: + // optional + // Type: + // array of strings + // Default: + // All types allowed + // Description: + // Whitelist of content MIME types supported. + // Popular MIME types include, but are not limited to “image/jpg” “image/gif”. + // Each implementing Exchange should have their own list of supported types in the integration docs. + // See Wikipedia's MIME page for more information and links to all IETF RFCs. + // If blank, assume all types are allowed. + MIMEs []string `json:"mimes,omitempty"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/request/image_asset_type.go b/native/request/image_asset_type.go new file mode 100644 index 0000000..d03033b --- /dev/null +++ b/native/request/image_asset_type.go @@ -0,0 +1,34 @@ +package request + +// 7.7 Image Asset Types +// +// Below is a list of common image asset element types of native advertising at the time of writing this spec. +// This list is non-exhaustive and intended to be extended by the buyers and sellers as the format evolves. +// +// An implementing exchange may not support all asset variants or may introduce new ones unique to that system. +// +// In order to facilitate adoption, recommendations are made for both minimum sizes and aspect ratios. +// We speak here of 'minimum maximum height' or ‘max height of at least’, which means the SSP should support a max height of at least this value. +// They are free to support larger, but the DSP knows that if they have an image of this size it will be accepted. +// Note that SSPs will be responsible for sizing image to exact size if min-maxheight framework is used; exact size may not be available at bid request time. +// Width is calculated from the 3 supported aspect ratios. +// Note we are merging the prior overlapping type 1 and type 2 as just type 1 - to be used for app icon, brand logo, or similar. +type ImageAssetType int64 + +const ( + ImageAssetTypeIcon ImageAssetType = 1 // Icon; Icon image; Optional. Max height: at least 50; aspect ratio: 1:1 + ImageAssetTypeLogo ImageAssetType = 2 // Logo; Logo image for the brand/app. To be deprecated in future version - use type 1 Icon. + + // Main; Large image preview for the ad. At least one of 2 size variants required: + // Small Variant: + // max height: at least 200 + // max width: at least 200, 267, or 382 + // aspect ratio: 1:1, 4:3, or 1.91:1 + // Large Variant: + // max height: at least 627 + // max width: at least 627, 836, or 1198 + // aspect ratio: 1:1, 4:3, or 1.91:1 + ImageAssetTypeMain ImageAssetType = 3 + + // 500+ XXX; Reserved for Exchange specific usage numbered above 500. No recommendations +) diff --git a/native/request/layout.go b/native/request/layout.go new file mode 100644 index 0000000..84b7a75 --- /dev/null +++ b/native/request/layout.go @@ -0,0 +1,22 @@ +package request + +// 7.1 Native Layout IDs - To Be Deprecated +// +// Layout ID is to be deprecated in a future version and is not suggested for new implementations. +// +// Below is a list of the core layouts described in the introduction above. +// +// An implementing exchange may not support all asset variants or introduce new ones unique to that system. +type Layout int64 + +const ( + LayoutContentWall Layout = 1 // Content Wall + LayoutAppWall Layout = 2 // App Wall + LayoutNewsFeed Layout = 3 // News Feed + LayoutChatList Layout = 4 // Chat List + LayoutCarousel Layout = 5 // Carousel + LayoutContentStream Layout = 6 // Content Stream + LayoutGrid Layout = 7 // Grid adjoining the content + +// 500+ Reserved for Exchange specific layouts. +) diff --git a/native/request/placement_type.go b/native/request/placement_type.go new file mode 100644 index 0000000..bdb1624 --- /dev/null +++ b/native/request/placement_type.go @@ -0,0 +1,15 @@ +package request + +// 7.5 Placement Type IDs +// +// The FORMAT of the ad you are purchasing, separate from the surrounding context +type PlacementType int64 + +const ( + PlacementTypeFeed = 1 // In the feed of content - for example as an item inside the organic feed/grid/listing/carousel. + PlacementTypeAtomicContentUnit = 2 // In the atomic unit of the content - IE in the article page or single image page + PlacementTypeOutsideCoreContent = 3 // Outside the core content - for example in the ads section on the right rail, as a banner-style placement near the content, etc. + PlacementTypeRecommendationWidget = 4 // Recommendation widget, most commonly presented below the article content. + + // 500+ To be defined by the exchange +) diff --git a/native/request/protocol.go b/native/request/protocol.go new file mode 100644 index 0000000..47dde72 --- /dev/null +++ b/native/request/protocol.go @@ -0,0 +1,19 @@ +package request + +// 5.8 Protocols (from OpenRTB spec 2.5) +// +// Options for the various bid response protocols that could be supported by an exchange. +type Protocol int8 + +const ( + ProtocolVAST10 Protocol = 1 // VAST 1.0 + ProtocolVAST20 Protocol = 2 // VAST 2.0 + ProtocolVAST30 Protocol = 3 // VAST 3.0 + ProtocolVAST10Wrapper Protocol = 4 // VAST 1.0 Wrapper + ProtocolVAST20Wrapper Protocol = 5 // VAST 2.0 Wrapper + ProtocolVAST30Wrapper Protocol = 6 // VAST 3.0 Wrapper + ProtocolVAST40 Protocol = 7 // VAST 4.0 + ProtocolVAST40Wrapper Protocol = 8 // VAST 4.0 Wrapper + ProtocolDAAST10 Protocol = 9 // DAAST 1.0 + ProtocolDAAST10Wrapper Protocol = 10 // DAAST 1.0 Wrapper +) diff --git a/native/request/raw_json.go b/native/request/raw_json.go new file mode 100644 index 0000000..d2d785a --- /dev/null +++ b/native/request/raw_json.go @@ -0,0 +1,27 @@ +package request + +import "errors" + +// RawJSON is a raw encoded JSON value. +// It implements encoding/json.Marshaler and encoding/json.Unmarshaler and can +// be used to delay JSON decoding or precompute a JSON encoding. +// +// Basically, it's just a copy of encoding/json.RawMessage type, +// but with more convenient non-pointer encoding. +// +// HEADS UP: this will be replaced with json.RawMessage when Go 1.10 is out. +type RawJSON []byte + +// MarshalJSON returns m as the JSON encoding of m. +func (m RawJSON) MarshalJSON() ([]byte, error) { + return m, nil +} + +// UnmarshalJSON sets *m to a copy of data. +func (m *RawJSON) UnmarshalJSON(data []byte) error { + if m == nil { + return errors.New("openrtb/native/request.RawJSON: UnmarshalJSON on nil pointer") + } + *m = append((*m)[0:0], data...) + return nil +} diff --git a/native/request/raw_json_test.go b/native/request/raw_json_test.go new file mode 100644 index 0000000..b5d1826 --- /dev/null +++ b/native/request/raw_json_test.go @@ -0,0 +1,57 @@ +package request_test + +import ( + "encoding/json" + + . "github.com/mxmCherry/openrtb/native/response" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("RawJSON", func() { + + var _ json.Marshaler = (RawJSON)(nil) + var _ json.Unmarshaler = (*RawJSON)(nil) + + It("should encode JSON", func() { + subject := RawJSON(`true`) + + actual, err := subject.MarshalJSON() + Expect(err).NotTo(HaveOccurred()) + Expect(actual).To(Equal([]byte(`true`))) + }) + + It("should decode JSON", func() { + subject := RawJSON(nil) + + err := subject.UnmarshalJSON([]byte(`true`)) + Expect(err).NotTo(HaveOccurred()) + Expect(subject).To(Equal(RawJSON(`true`))) + }) + + It("should decode JSON when embedded into struct", func() { + wrapper := struct { + Raw RawJSON `json:"raw"` + }{ + Raw: nil, + } + + err := json.Unmarshal([]byte(`{"raw":true}`), &wrapper) + Expect(err).NotTo(HaveOccurred()) + Expect(wrapper.Raw).To(Equal(RawJSON(`true`))) + }) + + It("should encode JSON when embedded into struct", func() { + wrapper := struct { + Raw RawJSON `json:"raw"` + }{ + Raw: RawJSON(`true`), + } + + actual, err := json.Marshal(wrapper) + Expect(err).NotTo(HaveOccurred()) + Expect(actual).To(MatchJSON(`{"raw":true}`)) + }) + +}) diff --git a/native/request/request.go b/native/request/request.go new file mode 100644 index 0000000..d846e5b --- /dev/null +++ b/native/request/request.go @@ -0,0 +1,130 @@ +// Package request provides OpenRTB Dynamic Native Ads API Specification Version 1.1 +// section 4 Native Ad Request Markup Details types: +// https://www.iab.com/guidelines/real-time-bidding-rtb-project/ +// https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-Native-Ads-Specification-1-1_2016.pdf +package request + +// 4.1 Native Markup Request Object +// +// The Native Object defines the native advertising opportunity available for bid via this bid request. +// It will be included as a JSON-encoded string in the bid request’s imp.native field or as a direct JSON object, depending on the choice of the exchange. +// While OpenRTB 2.3/2.4 supports only JSON-encoded strings, many exchanges have implemented a formal object. +// Check with your integration docs. +// +// The Default column dictates how optional parameters should be interpreted if explicit values are not provided. +type Request struct { + + // Field: + // ver + // Scope: + // optional + // Type: + // string + // Default: + // 1.1 + // Description: + // Version of the Native Markup version in use. + Ver string `json:"ver,omitempty"` + + // Field: + // layout + // Scope: + // recommended in 1.0, to be deprecated + // Type: + // integer + // Description: + // The Layout ID of the native ad unit. + // See the Table of Layout IDs below. + Layout Layout `json:"layout,omitempty"` + + // Field: + // adunit + // Scope: + // recommended in 1.0, to be deprecated + // Type: + // integer + // Description: + // The Ad unit ID of the native ad unit. + // See Table of Ad Unit IDs below for a list of supported core ad units. + AdUnit AdUnit `json:"adunit,omitempty"` + + // Field: + // context + // Scope: + // recommended + // Type: + // integer + // Description: + // The context in which the ad appears. + // See Table of Context IDs below for a list of supported context types. + Context ContextType `json:"context,omitempty"` + + // Field: + // contextsubtype + // Scope: + // optional + // Type: + // integer + // Description: + // A more detailed context in which the ad appears. + // See Table of Context SubType IDs below for a list of supported context subtypes. + ContextSubType ContextSubType `json:"contextsubtype,omitempty"` + + // Field: + // plcmttype + // Scope: + // recommended + // Type: + // integer + // Description: + // The design/format/layout of the ad unit being offered. + // See Table of Placement Type IDs below for a list of supported placement types. + PlcmtType PlacementType `json:"plcmttype,omitempty"` + + // Field: + // plcmtcnt + // Scope: + // optional + // Type: + // integer + // Default: + // 1 + // Description: + // The number of identical placements in this Layout. + // Refer Section 8.1 Multiplacement Bid Requests for further detail. + PlcmtCnt int64 `json:"plcmtcnt,omitempty"` + + // Field: + // seq + // Scope: + // optional + // Type: + // integer + // Default: + // 0 + // Description: + // 0 for the first ad, 1 for the second ad, and so on. + // Note this would generally NOT be used in combination with plcmtcnt - either you are auctioning multiple identical placements (in which case plcmtcnt>1, seq=0) or you are holding separate auctions for distinct items in the feed (in which case plcmtcnt=1, seq=>=1) + Seq int64 `json:"seq,omitempty"` + + // Field: + // assets + // Scope: + // required + // Type: + // array of objects + // Description: + // An array of Asset Objects. + // Any bid response must comply with the array of elements expressed in the bid request. + Assets []Asset `json:"assets"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/request/request_suite_test.go b/native/request/request_suite_test.go new file mode 100644 index 0000000..8a23350 --- /dev/null +++ b/native/request/request_suite_test.go @@ -0,0 +1,13 @@ +package request_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestRequest(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Request Suite") +} diff --git a/native/request/request_test.go b/native/request/request_test.go new file mode 100644 index 0000000..0cacb9e --- /dev/null +++ b/native/request/request_test.go @@ -0,0 +1,40 @@ +package request_test + +import ( + "encoding/json" + "io/ioutil" + "path/filepath" + + . "github.com/mxmCherry/openrtb/native/request" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/extensions/table" + . "github.com/onsi/gomega" +) + +var _ = Describe("Request", func() { + DescribeTable( + "Marshaling", + + func(filename string, subject interface{}) { + expected, err := ioutil.ReadFile(filepath.Join("testdata", filename)) + Expect(err).NotTo(HaveOccurred()) + + Expect(json.Unmarshal(expected, subject)).To(Succeed()) + + actual, err := json.Marshal(subject) + Expect(err).NotTo(HaveOccurred()) + + Expect(actual).To(MatchJSON(expected)) + }, + + Entry( + "Social Context", + "social-context.json", + new(Request)), + Entry( + "Content Context", + "content-context.json", + new(Request)), + ) +}) diff --git a/native/request/testdata/README.md b/native/request/testdata/README.md new file mode 100644 index 0000000..20c5d0b --- /dev/null +++ b/native/request/testdata/README.md @@ -0,0 +1,6 @@ +# testdata + +JSON examples copied from [OpenRTB](https://www.iab.com/guidelines/real-time-bidding-rtb-project/) [Dynamic Native Ads API +Specification Version 1.1](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-Native-Ads-Specification-1-1_2016.pdf) spec - section 6 Bid Request/Response Samples. + +Some empty/zero attributes were omited (not copied) because of [encoding/json](https://golang.org/pkg/encoding/json/) `omitempty` and [gomega.MatchJSON(...)](http://onsi.github.io/gomega/#matchjsonjson-interface). diff --git a/native/request/testdata/content-context.json b/native/request/testdata/content-context.json new file mode 100644 index 0000000..ff74236 --- /dev/null +++ b/native/request/testdata/content-context.json @@ -0,0 +1,63 @@ +{ + "ver": "1.1", + "context": 1, + "contextsubtype": 10, + "plcmttype": 11, + "plcmtcnt": 1, + "assets": [ + { + "id": 4, + "video": { + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3 + ], + "mimes": [ + "video/mp4" + ] + } + }, + { + "id": 123, + "required": 1, + "title": { + "len": 140 + } + }, + { + "id": 128, + "img": { + "wmin": 836, + "hmin": 627, + "type": 3 + } + }, + { + "id": 124, + "required": 1, + "img": { + "wmin": 50, + "hmin": 50, + "type": 1 + } + }, + { + "id": 126, + "required": 1, + "data": { + "type": 1, + "len": 25 + } + }, + { + "id": 127, + "required": 1, + "data": { + "type": 2, + "len": 140 + } + } + ] +} diff --git a/native/request/testdata/social-context.json b/native/request/testdata/social-context.json new file mode 100644 index 0000000..f30c10a --- /dev/null +++ b/native/request/testdata/social-context.json @@ -0,0 +1,49 @@ +{ + "ver": "1.1", + "context": 2, + "contextsubtype": 20, + "plcmttype": 11, + "plcmtcnt": 1, + "assets": [ + { + "id": 123, + "required": 1, + "title": { + "len": 140 + } + }, + { + "id": 128, + "img": { + "wmin": 836, + "hmin": 627, + "type": 3 + } + }, + { + "id": 124, + "required": 1, + "img": { + "wmin": 50, + "hmin": 50, + "type": 1 + } + }, + { + "id": 126, + "required": 1, + "data": { + "type": 1, + "len": 25 + } + }, + { + "id": 127, + "required": 1, + "data": { + "type": 2, + "len": 140 + } + } + ] +} diff --git a/native/request/title.go b/native/request/title.go new file mode 100644 index 0000000..1824512 --- /dev/null +++ b/native/request/title.go @@ -0,0 +1,27 @@ +package request + +// 4.3 Title Object +// +// The Title object is to be used for title element of the Native ad. +type Title struct { + // Field: + // len + // Scope: + // required + // Type: + // integer + // Description: + // Maximum length of the text in the title element. + // Recommended to be 25, 90, or 140. + Len int64 `json:"len"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/request/video.go b/native/request/video.go new file mode 100644 index 0000000..c1b0142 --- /dev/null +++ b/native/request/video.go @@ -0,0 +1,63 @@ +package request + +// 4.5 Video Object +// +// The video object to be used for all video elements supported in the Native Ad. +// This corresponds to the Video object of OpenRTB. +// Exchange implementers can impose their own specific restrictions. +// Here are the required attributes of the Video Object. +// For optional attributes please refer to OpenRTB. +type Video struct { + // Field: + // mimes + // Scope: + // required + // Type: + // array of string + // Description: + // Content MIME types supported. + // Popular MIME types include,but are not limited to “video/x-mswmv” for Windows Media, and “video/x-flv” for Flash Video, or “video/mp4”. + // Note that native frequently does not support flash. + MIMEs []string `json:"mimes"` + + // Field: + // minduration + // Scope: + // required + // Type: + // integer + // Description: + // Minimum video ad duration in seconds. + MinDuration int64 `json:"minduration"` + + // Field: + // maxduration + // Scope: + // required + // Type: + // integer + // Description: + // Maximum video ad duration in seconds. + MaxDuration int64 `json:"maxduration"` + + // Field: + // protocols + // Scope: + // required + // Type: + // array of integers + // Description: + // An array of video protocols the publisher can accept in the bid response. + // See OpenRTB Table ‘Video Bid Response Protocols’ for a list of possible values. + Protocols []Protocol `json:"protocols"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/response/README.md b/native/response/README.md new file mode 100644 index 0000000..1228aa3 --- /dev/null +++ b/native/response/README.md @@ -0,0 +1,4 @@ +# native/response [![GoDoc](https://godoc.org/github.com/mxmCherry/openrtb/native/response?status.svg)](https://godoc.org/github.com/mxmCherry/openrtb/native/response) + +[Go](https://golang.org/) implementation of [OpenRTB](https://www.iab.com/guidelines/real-time-bidding-rtb-project/) [Dynamic Native Ads API +Specification Version 1.1](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-Native-Ads-Specification-1-1_2016.pdf) section 5 Native Ad Response Markup Details types. diff --git a/native/response/asset.go b/native/response/asset.go new file mode 100644 index 0000000..075fd15 --- /dev/null +++ b/native/response/asset.go @@ -0,0 +1,95 @@ +package response + +// 5.2 Object: Asset +// +// Corresponds to the Asset Object in the request. The main container object for each asset +// requested or supported by Exchange on behalf of the rendering client. Any object that is +// required is to be flagged as such. Only one of the {title,img,video,data} objects should be +// present in each object. All others should be null/absent. The id is to be unique within the +// AssetObject array so that the response can be aligned. +type Asset struct { + // Field: + // id + // Scope: + // required + // Type: + // int + // Description: + // Unique asset ID, assigned by exchange, must match one of the asset IDs in request. + ID int64 `json:"id"` + + // Field: + // required + // Scope: + // optional + // Type: + // int + // Default: + // 0 + // Description: + // Set to 1 if asset is required. (bidder requires it to be displayed). + Required int8 `json:"required,omitempty"` + + // Field: + // title + // Scope: + // optional + // Type: + // object + // Description: + // Title object for title assets. + Title *Title `json:"title,omitempty"` + + // Field: + // img + // Scope: + // optional + // Type: + // object + // Description: + // Image object for image assets. + Img *Image `json:"img,omitempty"` + + // Field: + // video + // Scope: + // optional + // Type: + // object + // Description: + // Video object for video assets. See Video response object definition. + // Note that in-stream video ads are not part of Native. + // Native ads may contain a video as the ad creative itself. + Video *Video `json:"video,omitempty"` + + // Field: + // data + // Scope: + // optional + // Type: + // object + // Description: + // Data object for ratings, prices etc. + Data *Data `json:"data,omitempty"` + + // Field: + // link + // Scope: + // optional + // Type: + // object + // Description: + // Link object for call to actions. The link object applies if the asset item is activated (clicked). + // If there is no link object on the asset, the parent link object on the bid response applies. + Link *Link `json:"link,omitempty"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/response/data.go b/native/response/data.go new file mode 100644 index 0000000..c722e4b --- /dev/null +++ b/native/response/data.go @@ -0,0 +1,40 @@ +package response + +// 5.5 Object: Data +// +// Corresponds to the Data Object in the request, with the value filled in. The Data Object is to be +// used for all miscellaneous elements of the native unit such as Brand Name, Ratings, Review +// Count, Stars, Downloads, Price count etc. It is also generic for future native elements not +// contemplated at the time of the writing of this document. +type Data struct { + // Field: + // label + // Scope: + // optional + // Type: + // string + // Description: + // The optional formatted string name of the data type to be displayed. + Label string `json:"label,omitempty"` + + // Field: + // value + // Scope: + // required + // Type: + // string + // Description: + // The formatted string of data to be displayed. + // Can contain a formatted value such as "5 stars" or "$10" or "3.4 stars out of 5". + Value string `json:"value"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/response/image.go b/native/response/image.go new file mode 100644 index 0000000..25fa156 --- /dev/null +++ b/native/response/image.go @@ -0,0 +1,47 @@ +package response + +// 5.4 Object: Image +// +// Corresponds to the Image Object in the request. +// The Image object to be used for all image elements of the Native ad such as Icons, Main Image, etc. +type Image struct { + // Field: + // url + // Scope: + // required + // Type: + // string + // Description: + // URL of the image asset + URL string `json:"url"` + + // Field: + // w + // Scope: + // recommended + // Type: + // int + // Description: + // Width of the image in pixels + W uint64 `json:"w,omitempty"` + + // Field: + // h + // Scope: + // recommended + // Type: + // int + // Description: + // Height of the image in pixels + H uint64 `json:"h,omitempty"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/response/link.go b/native/response/link.go new file mode 100644 index 0000000..55edea9 --- /dev/null +++ b/native/response/link.go @@ -0,0 +1,49 @@ +package response + +// 5.7 Object: Link +// +// Used for ‘call to action’ assets, or other links from the Native ad. This Object should be +// associated to its peer object in the parent Asset Object or as the master link in the top level +// Native Ad response object. When that peer object is activated (clicked) the action should take +// the user to the location of the link. +type Link struct { + // Field: + // url + // Scope: + // required + // Type: + // string + // Description: + // Landing URL of the clickable link. + URL string `json:"url"` + + // Field: + // clicktrackers + // Scope: + // optional + // Type: + // string array + // Description: + // List of third-party tracker URLs to be fired on click of the URL. + ClickTrackers []string `json:"clicktrackers,omitempty"` + + // Field: + // fallback + // Scope: + // optional + // Type: + // string + // Description: + // Fallback URL for deeplink. To be used if the URL given in url is not supported by the device. + Fallback string `json:"fallback,omitempty"` + + // Field: + // ext + // Scope: + // optional + // Type: + // object + // Description: + // This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification + Ext RawJSON `json:"ext,omitempty"` +} diff --git a/native/response/raw_json.go b/native/response/raw_json.go new file mode 100644 index 0000000..8806ee2 --- /dev/null +++ b/native/response/raw_json.go @@ -0,0 +1,27 @@ +package response + +import "errors" + +// RawJSON is a raw encoded JSON value. +// It implements encoding/json.Marshaler and encoding/json.Unmarshaler and can +// be used to delay JSON decoding or precompute a JSON encoding. +// +// Basically, it's just a copy of encoding/json.RawMessage type, +// but with more convenient non-pointer encoding. +// +// HEADS UP: this will be replaced with json.RawMessage when Go 1.10 is out. +type RawJSON []byte + +// MarshalJSON returns m as the JSON encoding of m. +func (m RawJSON) MarshalJSON() ([]byte, error) { + return m, nil +} + +// UnmarshalJSON sets *m to a copy of data. +func (m *RawJSON) UnmarshalJSON(data []byte) error { + if m == nil { + return errors.New("openrtb/native/response.RawJSON: UnmarshalJSON on nil pointer") + } + *m = append((*m)[0:0], data...) + return nil +} diff --git a/native/response/raw_json_test.go b/native/response/raw_json_test.go new file mode 100644 index 0000000..735f62d --- /dev/null +++ b/native/response/raw_json_test.go @@ -0,0 +1,57 @@ +package response_test + +import ( + "encoding/json" + + . "github.com/mxmCherry/openrtb/native/response" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("RawJSON", func() { + + var _ json.Marshaler = (RawJSON)(nil) + var _ json.Unmarshaler = (*RawJSON)(nil) + + It("should encode JSON", func() { + subject := RawJSON(`true`) + + actual, err := subject.MarshalJSON() + Expect(err).NotTo(HaveOccurred()) + Expect(actual).To(Equal([]byte(`true`))) + }) + + It("should decode JSON", func() { + subject := RawJSON(nil) + + err := subject.UnmarshalJSON([]byte(`true`)) + Expect(err).NotTo(HaveOccurred()) + Expect(subject).To(Equal(RawJSON(`true`))) + }) + + It("should decode JSON when embedded into struct", func() { + wrapper := struct { + Raw RawJSON `json:"raw"` + }{ + Raw: nil, + } + + err := json.Unmarshal([]byte(`{"raw":true}`), &wrapper) + Expect(err).NotTo(HaveOccurred()) + Expect(wrapper.Raw).To(Equal(RawJSON(`true`))) + }) + + It("should encode JSON when embedded into struct", func() { + wrapper := struct { + Raw RawJSON `json:"raw"` + }{ + Raw: RawJSON(`true`), + } + + actual, err := json.Marshal(wrapper) + Expect(err).NotTo(HaveOccurred()) + Expect(actual).To(MatchJSON(`{"raw":true}`)) + }) + +}) diff --git a/native/response/response.go b/native/response/response.go new file mode 100644 index 0000000..8df516b --- /dev/null +++ b/native/response/response.go @@ -0,0 +1,76 @@ +// Package response provides OpenRTB Dynamic Native Ads API Specification Version 1.1 +// section 5 Native Ad Response Markup Details types: +// https://www.iab.com/guidelines/real-time-bidding-rtb-project/ +// https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-Native-Ads-Specification-1-1_2016.pdf +package response + +// 5.1 Object: Response +// +// The native object is the top level JSON object which identifies a native response. +type Response struct { + // Field: + // ver + // Scope: + // optional + // Type: + // string + // Default: + // 1.1 + // Description: + // Version of the Native Markup version in use. + Ver string `json:"ver,omitempty"` + + // Field: + // assets + // Scope: + // required + // Type: + // object array + // Description: + // List of native ad’s assets. + Assets []Asset `json:"assets"` + + // Field: + // link + // Scope: + // required + // Type: + // object + // Description: + // Destination Link. This is default link object for the ad. + // Individual assets can also have a link object which applies if the asset is activated(clicked). + // If the asset doesn’t have a link object, the parent link object applies. + Link Link `json:"link"` + + // Field: + // imptrackers + // Scope: + // optional + // Type: + // string array + // Description: + // Array of impression tracking URLs, expected to return a 1x1 image or 204 response - typically + // only passed when using 3rd party trackers. + ImpTrackers []string `json:"imptrackers,omitempty"` + + // Field: + // jstracker + // Scope: + // optional + // Type: + // string + // Description: + // Optional JavaScript impression tracker. This is a valid HTML, Javascript is already wrapped in