Skip to content

Commit 6958c3a

Browse files
committed
Added tests for the REST endpoints exposed in our example. Added a script for running the example app. Split the example into multiple files.
1 parent 4898d2f commit 6958c3a

File tree

6 files changed

+326
-218
lines changed

6 files changed

+326
-218
lines changed

examples/app.go

Lines changed: 6 additions & 218 deletions
Original file line numberDiff line numberDiff line change
@@ -8,98 +8,11 @@ import (
88
"io/ioutil"
99
"net/http"
1010
"net/http/httptest"
11-
"reflect"
12-
"regexp"
13-
"strconv"
1411
"time"
1512

1613
"github.com/google/jsonapi"
1714
)
1815

19-
func createBlog(w http.ResponseWriter, r *http.Request) {
20-
jsonapiRuntime := jsonapi.NewRuntime().Instrument("blogs.create")
21-
22-
blog := new(Blog)
23-
24-
if err := jsonapiRuntime.UnmarshalPayload(r.Body, blog); err != nil {
25-
http.Error(w, err.Error(), http.StatusInternalServerError)
26-
return
27-
}
28-
29-
// ...do stuff with your blog...
30-
31-
w.WriteHeader(http.StatusCreated)
32-
w.Header().Set("Content-Type", jsonapi.MediaType)
33-
34-
if err := jsonapiRuntime.MarshalOnePayload(w, blog); err != nil {
35-
http.Error(w, err.Error(), http.StatusInternalServerError)
36-
}
37-
}
38-
39-
func listBlogs(w http.ResponseWriter, r *http.Request) {
40-
jsonapiRuntime := jsonapi.NewRuntime().Instrument("blogs.list")
41-
// ...fetch your blogs, filter, offset, limit, etc...
42-
43-
// but, for now
44-
blogs := testBlogsForList()
45-
46-
w.WriteHeader(http.StatusOK)
47-
w.Header().Set("Content-Type", jsonapi.MediaType)
48-
if err := jsonapiRuntime.MarshalManyPayload(w, blogs); err != nil {
49-
http.Error(w, err.Error(), http.StatusInternalServerError)
50-
}
51-
}
52-
53-
func showBlog(w http.ResponseWriter, r *http.Request) {
54-
id := r.FormValue("id")
55-
56-
// ...fetch your blog...
57-
58-
intID, err := strconv.Atoi(id)
59-
if err != nil {
60-
http.Error(w, err.Error(), http.StatusInternalServerError)
61-
return
62-
}
63-
64-
jsonapiRuntime := jsonapi.NewRuntime().Instrument("blogs.show")
65-
66-
// but, for now
67-
blog := testBlogForCreate(intID)
68-
w.WriteHeader(http.StatusOK)
69-
70-
w.Header().Set("Content-Type", jsonapi.MediaType)
71-
if err := jsonapiRuntime.MarshalOnePayload(w, blog); err != nil {
72-
http.Error(w, err.Error(), http.StatusInternalServerError)
73-
}
74-
}
75-
76-
func echoBlogs(w http.ResponseWriter, r *http.Request) {
77-
jsonapiRuntime := jsonapi.NewRuntime().Instrument("blogs.echo")
78-
79-
// Fetch the blogs from the HTTP request body
80-
data, err := jsonapiRuntime.UnmarshalManyPayload(r.Body, reflect.TypeOf(new(Blog)))
81-
if err != nil {
82-
http.Error(w, err.Error(), http.StatusInternalServerError)
83-
}
84-
85-
// Type assert the []interface{} to []*Blog
86-
blogs := []*Blog{}
87-
for _, b := range data {
88-
blog, ok := b.(*Blog)
89-
if !ok {
90-
http.Error(w, "Unexpected type", http.StatusInternalServerError)
91-
}
92-
blogs = append(blogs, blog)
93-
}
94-
95-
// Echo the blogs to the response body
96-
w.WriteHeader(http.StatusOK)
97-
w.Header().Set("Content-Type", jsonapi.MediaType)
98-
if err := jsonapiRuntime.MarshalManyPayload(w, blogs); err != nil {
99-
http.Error(w, err.Error(), http.StatusInternalServerError)
100-
}
101-
}
102-
10316
func main() {
10417
jsonapi.Instrumentation = func(r *jsonapi.Runtime, eventType jsonapi.Event, callGUID string, dur time.Duration) {
10518
metricPrefix := r.Value("instrument").(string)
@@ -121,91 +34,11 @@ func main() {
12134
}
12235
}
12336

124-
http.HandleFunc("/blogs", func(w http.ResponseWriter, r *http.Request) {
125-
if !regexp.MustCompile(`application/vnd\.api\+json`).Match([]byte(r.Header.Get("Accept"))) {
126-
http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
127-
return
128-
}
129-
130-
if r.Method == http.MethodPost {
131-
createBlog(w, r)
132-
} else if r.Method == http.MethodPut {
133-
echoBlogs(w, r)
134-
} else if r.FormValue("id") != "" {
135-
showBlog(w, r)
136-
} else {
137-
listBlogs(w, r)
138-
}
139-
})
140-
37+
exampleHandler := &ExampleHandler{}
38+
http.HandleFunc("/blogs", exampleHandler.ServeHTTP)
14139
exerciseHandler()
14240
}
14341

144-
func testBlogForCreate(i int) *Blog {
145-
return &Blog{
146-
ID: 1 * i,
147-
Title: "Title 1",
148-
CreatedAt: time.Now(),
149-
Posts: []*Post{
150-
&Post{
151-
ID: 1 * i,
152-
Title: "Foo",
153-
Body: "Bar",
154-
Comments: []*Comment{
155-
&Comment{
156-
ID: 1 * i,
157-
Body: "foo",
158-
},
159-
&Comment{
160-
ID: 2 * i,
161-
Body: "bar",
162-
},
163-
},
164-
},
165-
&Post{
166-
ID: 2 * i,
167-
Title: "Fuubar",
168-
Body: "Bas",
169-
Comments: []*Comment{
170-
&Comment{
171-
ID: 1 * i,
172-
Body: "foo",
173-
},
174-
&Comment{
175-
ID: 3 * i,
176-
Body: "bas",
177-
},
178-
},
179-
},
180-
},
181-
CurrentPost: &Post{
182-
ID: 1 * i,
183-
Title: "Foo",
184-
Body: "Bar",
185-
Comments: []*Comment{
186-
&Comment{
187-
ID: 1 * i,
188-
Body: "foo",
189-
},
190-
&Comment{
191-
ID: 2 * i,
192-
Body: "bar",
193-
},
194-
},
195-
},
196-
}
197-
}
198-
199-
func testBlogsForList() []interface{} {
200-
blogs := make([]interface{}, 0, 10)
201-
202-
for i := 0; i < 10; i++ {
203-
blogs = append(blogs, testBlogForCreate(i))
204-
}
205-
206-
return blogs
207-
}
208-
20942
func exerciseHandler() {
21043
// list
21144
req, _ := http.NewRequest(http.MethodGet, "/blogs", nil)
@@ -242,7 +75,7 @@ func exerciseHandler() {
24275
fmt.Println("============== end raw jsonapi from show =============")
24376

24477
// create
245-
blog := testBlogForCreate(1)
78+
blog := fixtureBlogCreate(1)
24679
in := bytes.NewBuffer(nil)
24780
jsonapi.MarshalOnePayloadEmbedded(in, blog)
24881

@@ -265,9 +98,9 @@ func exerciseHandler() {
26598

26699
// echo
267100
blogs := []interface{}{
268-
testBlogForCreate(1),
269-
testBlogForCreate(2),
270-
testBlogForCreate(3),
101+
fixtureBlogCreate(1),
102+
fixtureBlogCreate(2),
103+
fixtureBlogCreate(3),
271104
}
272105
in = bytes.NewBuffer(nil)
273106
jsonapi.MarshalManyPayload(in, blogs)
@@ -300,48 +133,3 @@ func exerciseHandler() {
300133
fmt.Println(string(out.Bytes()))
301134
fmt.Println("================ end marshal materialized Blog struct =================")
302135
}
303-
304-
type Blog struct {
305-
ID int `jsonapi:"primary,blogs"`
306-
Title string `jsonapi:"attr,title"`
307-
Posts []*Post `jsonapi:"relation,posts"`
308-
CurrentPost *Post `jsonapi:"relation,current_post"`
309-
CurrentPostID int `jsonapi:"attr,current_post_id"`
310-
CreatedAt time.Time `jsonapi:"attr,created_at"`
311-
ViewCount int `jsonapi:"attr,view_count"`
312-
}
313-
314-
type Post struct {
315-
ID int `jsonapi:"primary,posts"`
316-
BlogID int `jsonapi:"attr,blog_id"`
317-
Title string `jsonapi:"attr,title"`
318-
Body string `jsonapi:"attr,body"`
319-
Comments []*Comment `jsonapi:"relation,comments"`
320-
}
321-
322-
type Comment struct {
323-
ID int `jsonapi:"primary,comments"`
324-
PostID int `jsonapi:"attr,post_id"`
325-
Body string `jsonapi:"attr,body"`
326-
}
327-
328-
// Blog Links
329-
func (blog Blog) JSONAPILinks() *map[string]interface{} {
330-
return &map[string]interface{}{
331-
"self": fmt.Sprintf("https://example.com/blogs/%d", blog.ID),
332-
}
333-
}
334-
335-
func (blog Blog) JSONAPIRelationshipLinks(relation string) *map[string]interface{} {
336-
if relation == "posts" {
337-
return &map[string]interface{}{
338-
"related": fmt.Sprintf("https://example.com/blogs/%d/posts", blog.ID),
339-
}
340-
}
341-
if relation == "current_post" {
342-
return &map[string]interface{}{
343-
"related": fmt.Sprintf("https://example.com/blogs/%d/current_post", blog.ID),
344-
}
345-
}
346-
return nil
347-
}

examples/fixtures.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package main
2+
3+
import "time"
4+
5+
func fixtureBlogCreate(i int) *Blog {
6+
return &Blog{
7+
ID: 1 * i,
8+
Title: "Title 1",
9+
CreatedAt: time.Now(),
10+
Posts: []*Post{
11+
&Post{
12+
ID: 1 * i,
13+
Title: "Foo",
14+
Body: "Bar",
15+
Comments: []*Comment{
16+
&Comment{
17+
ID: 1 * i,
18+
Body: "foo",
19+
},
20+
&Comment{
21+
ID: 2 * i,
22+
Body: "bar",
23+
},
24+
},
25+
},
26+
&Post{
27+
ID: 2 * i,
28+
Title: "Fuubar",
29+
Body: "Bas",
30+
Comments: []*Comment{
31+
&Comment{
32+
ID: 1 * i,
33+
Body: "foo",
34+
},
35+
&Comment{
36+
ID: 3 * i,
37+
Body: "bas",
38+
},
39+
},
40+
},
41+
},
42+
CurrentPost: &Post{
43+
ID: 1 * i,
44+
Title: "Foo",
45+
Body: "Bar",
46+
Comments: []*Comment{
47+
&Comment{
48+
ID: 1 * i,
49+
Body: "foo",
50+
},
51+
&Comment{
52+
ID: 2 * i,
53+
Body: "bar",
54+
},
55+
},
56+
},
57+
}
58+
}
59+
60+
func fixtureBlogsList() (blogs []interface{}) {
61+
for i := 0; i < 10; i++ {
62+
blogs = append(blogs, fixtureBlogCreate(i))
63+
}
64+
65+
return blogs
66+
}

0 commit comments

Comments
 (0)