Skip to content

Commit

Permalink
Added tests for the REST endpoints exposed in our example. Added a sc…
Browse files Browse the repository at this point in the history
…ript for running the example app. Split the example into multiple files.
  • Loading branch information
aren55555 committed Feb 17, 2017
1 parent 4898d2f commit 6958c3a
Show file tree
Hide file tree
Showing 6 changed files with 326 additions and 218 deletions.
224 changes: 6 additions & 218 deletions examples/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,98 +8,11 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"regexp"
"strconv"
"time"

"github.com/google/jsonapi"
)

func createBlog(w http.ResponseWriter, r *http.Request) {
jsonapiRuntime := jsonapi.NewRuntime().Instrument("blogs.create")

blog := new(Blog)

if err := jsonapiRuntime.UnmarshalPayload(r.Body, blog); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// ...do stuff with your blog...

w.WriteHeader(http.StatusCreated)
w.Header().Set("Content-Type", jsonapi.MediaType)

if err := jsonapiRuntime.MarshalOnePayload(w, blog); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

func listBlogs(w http.ResponseWriter, r *http.Request) {
jsonapiRuntime := jsonapi.NewRuntime().Instrument("blogs.list")
// ...fetch your blogs, filter, offset, limit, etc...

// but, for now
blogs := testBlogsForList()

w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", jsonapi.MediaType)
if err := jsonapiRuntime.MarshalManyPayload(w, blogs); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

func showBlog(w http.ResponseWriter, r *http.Request) {
id := r.FormValue("id")

// ...fetch your blog...

intID, err := strconv.Atoi(id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

jsonapiRuntime := jsonapi.NewRuntime().Instrument("blogs.show")

// but, for now
blog := testBlogForCreate(intID)
w.WriteHeader(http.StatusOK)

w.Header().Set("Content-Type", jsonapi.MediaType)
if err := jsonapiRuntime.MarshalOnePayload(w, blog); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

func echoBlogs(w http.ResponseWriter, r *http.Request) {
jsonapiRuntime := jsonapi.NewRuntime().Instrument("blogs.echo")

// Fetch the blogs from the HTTP request body
data, err := jsonapiRuntime.UnmarshalManyPayload(r.Body, reflect.TypeOf(new(Blog)))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}

// Type assert the []interface{} to []*Blog
blogs := []*Blog{}
for _, b := range data {
blog, ok := b.(*Blog)
if !ok {
http.Error(w, "Unexpected type", http.StatusInternalServerError)
}
blogs = append(blogs, blog)
}

// Echo the blogs to the response body
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", jsonapi.MediaType)
if err := jsonapiRuntime.MarshalManyPayload(w, blogs); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

func main() {
jsonapi.Instrumentation = func(r *jsonapi.Runtime, eventType jsonapi.Event, callGUID string, dur time.Duration) {
metricPrefix := r.Value("instrument").(string)
Expand All @@ -121,91 +34,11 @@ func main() {
}
}

http.HandleFunc("/blogs", func(w http.ResponseWriter, r *http.Request) {
if !regexp.MustCompile(`application/vnd\.api\+json`).Match([]byte(r.Header.Get("Accept"))) {
http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
return
}

if r.Method == http.MethodPost {
createBlog(w, r)
} else if r.Method == http.MethodPut {
echoBlogs(w, r)
} else if r.FormValue("id") != "" {
showBlog(w, r)
} else {
listBlogs(w, r)
}
})

exampleHandler := &ExampleHandler{}
http.HandleFunc("/blogs", exampleHandler.ServeHTTP)
exerciseHandler()
}

func testBlogForCreate(i int) *Blog {
return &Blog{
ID: 1 * i,
Title: "Title 1",
CreatedAt: time.Now(),
Posts: []*Post{
&Post{
ID: 1 * i,
Title: "Foo",
Body: "Bar",
Comments: []*Comment{
&Comment{
ID: 1 * i,
Body: "foo",
},
&Comment{
ID: 2 * i,
Body: "bar",
},
},
},
&Post{
ID: 2 * i,
Title: "Fuubar",
Body: "Bas",
Comments: []*Comment{
&Comment{
ID: 1 * i,
Body: "foo",
},
&Comment{
ID: 3 * i,
Body: "bas",
},
},
},
},
CurrentPost: &Post{
ID: 1 * i,
Title: "Foo",
Body: "Bar",
Comments: []*Comment{
&Comment{
ID: 1 * i,
Body: "foo",
},
&Comment{
ID: 2 * i,
Body: "bar",
},
},
},
}
}

func testBlogsForList() []interface{} {
blogs := make([]interface{}, 0, 10)

for i := 0; i < 10; i++ {
blogs = append(blogs, testBlogForCreate(i))
}

return blogs
}

func exerciseHandler() {
// list
req, _ := http.NewRequest(http.MethodGet, "/blogs", nil)
Expand Down Expand Up @@ -242,7 +75,7 @@ func exerciseHandler() {
fmt.Println("============== end raw jsonapi from show =============")

// create
blog := testBlogForCreate(1)
blog := fixtureBlogCreate(1)
in := bytes.NewBuffer(nil)
jsonapi.MarshalOnePayloadEmbedded(in, blog)

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

// echo
blogs := []interface{}{
testBlogForCreate(1),
testBlogForCreate(2),
testBlogForCreate(3),
fixtureBlogCreate(1),
fixtureBlogCreate(2),
fixtureBlogCreate(3),
}
in = bytes.NewBuffer(nil)
jsonapi.MarshalManyPayload(in, blogs)
Expand Down Expand Up @@ -300,48 +133,3 @@ func exerciseHandler() {
fmt.Println(string(out.Bytes()))
fmt.Println("================ end marshal materialized Blog struct =================")
}

type Blog struct {
ID int `jsonapi:"primary,blogs"`
Title string `jsonapi:"attr,title"`
Posts []*Post `jsonapi:"relation,posts"`
CurrentPost *Post `jsonapi:"relation,current_post"`
CurrentPostID int `jsonapi:"attr,current_post_id"`
CreatedAt time.Time `jsonapi:"attr,created_at"`
ViewCount int `jsonapi:"attr,view_count"`
}

type Post struct {
ID int `jsonapi:"primary,posts"`
BlogID int `jsonapi:"attr,blog_id"`
Title string `jsonapi:"attr,title"`
Body string `jsonapi:"attr,body"`
Comments []*Comment `jsonapi:"relation,comments"`
}

type Comment struct {
ID int `jsonapi:"primary,comments"`
PostID int `jsonapi:"attr,post_id"`
Body string `jsonapi:"attr,body"`
}

// Blog Links
func (blog Blog) JSONAPILinks() *map[string]interface{} {
return &map[string]interface{}{
"self": fmt.Sprintf("https://example.com/blogs/%d", blog.ID),
}
}

func (blog Blog) JSONAPIRelationshipLinks(relation string) *map[string]interface{} {
if relation == "posts" {
return &map[string]interface{}{
"related": fmt.Sprintf("https://example.com/blogs/%d/posts", blog.ID),
}
}
if relation == "current_post" {
return &map[string]interface{}{
"related": fmt.Sprintf("https://example.com/blogs/%d/current_post", blog.ID),
}
}
return nil
}
66 changes: 66 additions & 0 deletions examples/fixtures.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import "time"

func fixtureBlogCreate(i int) *Blog {
return &Blog{
ID: 1 * i,
Title: "Title 1",
CreatedAt: time.Now(),
Posts: []*Post{
&Post{
ID: 1 * i,
Title: "Foo",
Body: "Bar",
Comments: []*Comment{
&Comment{
ID: 1 * i,
Body: "foo",
},
&Comment{
ID: 2 * i,
Body: "bar",
},
},
},
&Post{
ID: 2 * i,
Title: "Fuubar",
Body: "Bas",
Comments: []*Comment{
&Comment{
ID: 1 * i,
Body: "foo",
},
&Comment{
ID: 3 * i,
Body: "bas",
},
},
},
},
CurrentPost: &Post{
ID: 1 * i,
Title: "Foo",
Body: "Bar",
Comments: []*Comment{
&Comment{
ID: 1 * i,
Body: "foo",
},
&Comment{
ID: 2 * i,
Body: "bar",
},
},
},
}
}

func fixtureBlogsList() (blogs []interface{}) {
for i := 0; i < 10; i++ {
blogs = append(blogs, fixtureBlogCreate(i))
}

return blogs
}
Loading

0 comments on commit 6958c3a

Please sign in to comment.