Skip to content

Commit dd3eb47

Browse files
committed
Added docs. Added getters with defaults.
1 parent c3578dd commit dd3eb47

File tree

5 files changed

+258
-26
lines changed

5 files changed

+258
-26
lines changed

doc.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) 2013 Black Square Media Ltd. All rights reserved.
2+
// (The MIT License)
3+
4+
// Permission is hereby granted, free of charge, to any person obtaining
5+
// a copy of this software and associated documentation files (the
6+
// 'Software'), to deal in the Software without restriction, including
7+
// without limitation the rights to use, copy, modify, merge, publish,
8+
// distribute, sublicense, and/or sell copies of the Software, and to
9+
// permit persons to whom the Software is furnished to do so, subject to
10+
// the following conditions:
11+
12+
// The above copyright notice and this permission notice shall be
13+
// included in all copies or substantial portions of the Software.
14+
15+
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18+
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19+
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20+
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21+
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22+
23+
/*
24+
This package implements a parser (with optional validation) and a generator for
25+
OpenRTB 2.1 requests and responses.
26+
27+
Spec
28+
29+
http://www.iab.net/media/file/OpenRTB-API-Specification-Version-2-1-FINAL.pdf
30+
31+
Usage
32+
33+
// Handle a HTTP request
34+
http.HandleFunc("/bid", func(w http.ResponseWriter, r *http.Request) {
35+
defer r.Body().Close()
36+
37+
req, err := openrtb.ParseRequest(r.Body(), true)
38+
if err != nil {
39+
log.Println("ERROR %s", err.Error())
40+
} else {
41+
log.Println("INFO Received bid request %s", *req.Id)
42+
}
43+
44+
w.WriteHeader(204) // respond with 'no bid'
45+
})
46+
47+
Implemented
48+
49+
- Request parsing
50+
- Most objects
51+
- PMP additions
52+
53+
Todo
54+
55+
- Response parsing
56+
- Generating request and responses
57+
- Content objects
58+
*/
59+
package openrtb

openrtb.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,22 @@ type Banner struct {
111111
Ext Extensions
112112
}
113113

114+
// Returns topframe status, with default fallback
115+
func (b *Banner) IsTopFrame() bool {
116+
if b.Topframe != nil {
117+
return *b.Topframe == 1
118+
}
119+
return false
120+
}
121+
122+
// Returns the position, with default fallback
123+
func (b *Banner) Position() int {
124+
if b.Pos != nil {
125+
return *b.Pos
126+
}
127+
return AD_POS_UNKNOWN
128+
}
129+
114130
// The "video" object must be included directly in the impression object if the impression offered
115131
// for auction is an in-stream video ad opportunity.
116132
type Video struct {
@@ -137,6 +153,30 @@ type Video struct {
137153
Ext Extensions
138154
}
139155

156+
// Returns the sequence number, with default fallback
157+
func (v *Video) Seq() int {
158+
if v.Sequence != nil {
159+
return *v.Sequence
160+
}
161+
return 1
162+
}
163+
164+
// Returns the boxing permission status, with default fallback
165+
func (v *Video) IsBoxingAllowed() bool {
166+
if v.Boxingallowed != nil {
167+
return *v.Boxingallowed == 1
168+
}
169+
return true
170+
}
171+
172+
// Returns the position, with default fallback
173+
func (v *Video) Position() int {
174+
if v.Pos != nil {
175+
return *v.Pos
176+
}
177+
return AD_POS_UNKNOWN
178+
}
179+
140180
// A site object should be included if the ad supported content is part of a website (as opposed to
141181
// an application). A bid request must not contain both a site object and an app object.
142182
type Site struct {
@@ -156,6 +196,14 @@ type Site struct {
156196
Ext Extensions
157197
}
158198

199+
// Returns the privacy policy status, with default fallback
200+
func (s *Site) IsPrivacyPolicy() bool {
201+
if s.Privacypolicy != nil {
202+
return *s.Privacypolicy == 1
203+
}
204+
return false
205+
}
206+
159207
// An "app" object should be included if the ad supported content is part of a mobile application
160208
// (as opposed to a mobile website). A bid request must not contain both an "app" object and a
161209
// "site" object.
@@ -177,6 +225,22 @@ type App struct {
177225
Ext Extensions
178226
}
179227

228+
// Returns the privacy policy status, with default fallback
229+
func (a *App) IsPrivacyPolicy() bool {
230+
if a.Privacypolicy != nil {
231+
return *a.Privacypolicy == 1
232+
}
233+
return false
234+
}
235+
236+
// Returns the paid status, with default fallback
237+
func (a *App) IsPaid() bool {
238+
if a.Paid != nil {
239+
return *a.Paid == 1
240+
}
241+
return false
242+
}
243+
180244
// This object may be useful in the situation where syndicated content contains impressions and
181245
// does not necessarily match the publisher’s general content. The exchange might or might not
182246
// have knowledge of the page where the content is running, as a result of the syndication
@@ -230,6 +294,38 @@ type Device struct {
230294
Ext map[string]string
231295
}
232296

297+
// Returns the DNT status, with default fallback
298+
func (d *Device) IsDnt() bool {
299+
if d.Dnt != nil {
300+
return *d.Dnt == 1
301+
}
302+
return false
303+
}
304+
305+
// Returns the JS status, with default fallback
306+
func (d *Device) IsJs() bool {
307+
if d.Js != nil {
308+
return *d.Js == 1
309+
}
310+
return false
311+
}
312+
313+
// Returns the connection type, with default fallback
314+
func (d *Device) ConnectionType() int {
315+
if d.Connectiontype != nil {
316+
return *d.Connectiontype
317+
}
318+
return CONN_TYPE_UNKNOWN
319+
}
320+
321+
// Returns the connection type, with default fallback
322+
func (d *Device) DeviceType() int {
323+
if d.Devicetype != nil {
324+
return *d.Devicetype
325+
}
326+
return DEVICE_TYPE_UNKNOWN
327+
}
328+
233329
// Note that the Geo Object may appear in one or both the Device Object and the User Object.
234330
// This is intentional, since the information may be derived from either a device-oriented source
235331
// (such as IP geo lookup), or by user registration information (for example provided to a publisher

openrtb_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package openrtb
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"testing"
6+
)
7+
8+
func TestBanner_IsTopFrame(t *testing.T) {
9+
b := &Banner{}
10+
assert.Equal(t, b.IsTopFrame(), false)
11+
}
12+
13+
func TestBanner_Position(t *testing.T) {
14+
b := &Banner{}
15+
assert.Equal(t, b.Position(), 0)
16+
}
17+
18+
func TestVideo_Seq(t *testing.T) {
19+
v := &Video{}
20+
assert.Equal(t, v.Seq(), 1)
21+
}
22+
23+
func TestVideo_IsBoxingAllowed(t *testing.T) {
24+
v := &Video{}
25+
assert.Equal(t, v.IsBoxingAllowed(), true)
26+
}
27+
28+
func TestVideo_Position(t *testing.T) {
29+
v := &Video{}
30+
assert.Equal(t, v.Position(), 0)
31+
}
32+
33+
func TestSite_IsPrivacyPolicy(t *testing.T) {
34+
s := &Site{}
35+
assert.Equal(t, s.IsPrivacyPolicy(), false)
36+
}
37+
38+
func TestApp_IsPrivacyPolicy(t *testing.T) {
39+
a := &App{}
40+
assert.Equal(t, a.IsPrivacyPolicy(), false)
41+
}
42+
43+
func TestApp_IsPaid(t *testing.T) {
44+
a := &App{}
45+
assert.Equal(t, a.IsPaid(), false)
46+
}
47+
48+
func TestDevice_IsDnt(t *testing.T) {
49+
d := &Device{}
50+
assert.Equal(t, d.IsDnt(), false)
51+
}
52+
53+
func TestDevice_IsJs(t *testing.T) {
54+
d := &Device{}
55+
assert.Equal(t, d.IsJs(), false)
56+
}
57+
58+
func TestDevice_ConnectionType(t *testing.T) {
59+
d := &Device{}
60+
assert.Equal(t, d.ConnectionType(), 0)
61+
}
62+
63+
func TestDevice_DeviceType(t *testing.T) {
64+
d := &Device{}
65+
assert.Equal(t, d.DeviceType(), 0)
66+
}

parser.go

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package openrtb
33
import (
44
"encoding/json"
55
"errors"
6+
"io"
67
)
78

89
var (
@@ -19,26 +20,37 @@ var (
1920
errValidationVideoProtocol = errors.New("openrtb parse: video protocol missing")
2021
)
2122

22-
// Parses a raw JSON byte array into a standard-compliant
23-
// OpenRTB Request struct
24-
func ParseRequest(rawJson []byte, validate bool) (req *Request, err error) {
25-
if err = json.Unmarshal(rawJson, &req); err != nil {
23+
// Parses an OpenRTB Request struct from an io.Reader
24+
// Optionally validates and applies defaults to the Request object (recommended)
25+
func ParseRequest(reader io.Reader, validate bool) (req *Request, err error) {
26+
dec := json.NewDecoder(reader)
27+
if err = dec.Decode(&req); err != nil {
2628
return nil, err
2729
}
30+
return processReq(req, validate)
31+
}
2832

29-
if validate {
30-
err = validateReq(req)
33+
// Parses an OpenRTB Request from bytes
34+
// Optionally validates and applies defaults to the Request object (recommended)
35+
func ParseRequestBytes(data []byte, validate bool) (req *Request, err error) {
36+
if err = json.Unmarshal(data, &req); err != nil {
37+
return nil, err
3138
}
39+
return processReq(req, validate)
40+
}
3241

33-
if err != nil {
34-
return nil, err
42+
// ------------------- Request Helpers ----------------------
43+
44+
func processReq(req *Request, validate bool) (*Request, error) {
45+
if validate {
46+
if err := validateReq(req); err != nil {
47+
return nil, err
48+
}
3549
}
3650

3751
return req, nil
3852
}
3953

40-
// ------------------- Validation Helpers ----------------------
41-
4254
func validateReq(req *Request) error {
4355

4456
// Validations
@@ -151,18 +163,10 @@ func validateVideo(video *Video) error {
151163
video.Boxingallowed = new(int)
152164
*video.Boxingallowed = 1
153165
}
154-
if video.Boxingallowed == nil {
155-
video.Boxingallowed = new(int)
156-
*video.Boxingallowed = 1
157-
}
158166
if video.Pos == nil {
159167
video.Pos = new(int)
160168
*video.Pos = AD_POS_UNKNOWN
161169
}
162-
if video.Startdelay == nil {
163-
video.Startdelay = new(int)
164-
*video.Startdelay = VIDEO_START_DELAY_PRE_ROLL
165-
}
166170

167171
return nil
168172
}

parser_test.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
package openrtb
22

33
import (
4+
"bytes"
45
"github.com/stretchr/testify/assert"
56
"testing"
67
)
78

8-
func TestParseRequest_BlankUnvalidated(t *testing.T) {
9-
req, err := ParseRequest([]byte("{}"), false)
9+
func TestParseRequestBytes_BlankUnvalidated(t *testing.T) {
10+
req, err := ParseRequestBytes([]byte("{}"), false)
1011
assert.Nil(t, err)
1112
assert.IsType(t, &Request{}, req)
1213
}
1314

14-
func TestParseRequest_BlankValidated(t *testing.T) {
15-
req, err := ParseRequest([]byte("{}"), true)
15+
func TestParseRequestBytes_BlankValidated(t *testing.T) {
16+
req, err := ParseRequestBytes([]byte("{}"), true)
1617
assert.Nil(t, req)
1718
assert.Equal(t, err.Error(), "openrtb parse: request ID missing")
1819
}
1920

20-
func TestParseRequest_SimpleBanner(t *testing.T) {
21-
req, err := ParseRequest(simpleBanner, true)
21+
func TestParseRequestBytes_SimpleBanner(t *testing.T) {
22+
req, err := ParseRequestBytes(simpleBanner, true)
2223
assert.Nil(t, err)
2324
assert.IsType(t, &Request{}, req)
2425

@@ -37,8 +38,8 @@ func TestParseRequest_SimpleBanner(t *testing.T) {
3738
assert.Equal(t, *req.User.Buyeruid, "5df678asd8987656asdf78987654")
3839
}
3940

40-
func TestParseExpandableCreative(t *testing.T) {
41-
req, err := ParseRequest(expandableCreative, true)
41+
func TestParseRequestBytes_ExpandableCreative(t *testing.T) {
42+
req, err := ParseRequestBytes(expandableCreative, true)
4243
assert.Nil(t, err)
4344
assert.IsType(t, &Request{}, req)
4445

@@ -53,6 +54,12 @@ func TestParseExpandableCreative(t *testing.T) {
5354
assert.Equal(t, *req.User.Data[0].Segment[2].Id, "23423424")
5455
}
5556

57+
func TestParseRequest_ExpandableCreative(t *testing.T) {
58+
req, err := ParseRequest(bytes.NewBuffer(expandableCreative), true)
59+
assert.Nil(t, err)
60+
assert.IsType(t, &Request{}, req)
61+
}
62+
5663
var simpleBanner []byte = []byte(`
5764
{
5865
"id":"1234534625254",

0 commit comments

Comments
 (0)