<commit_before>// Copyright 2013 Julien Schmidt. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.

package httprouter

import (
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
)

type mockResponseWriter struct{}

func (m *mockResponseWriter) Header() (h http.Header) {
	return http.Header{}
}

func (m *mockResponseWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
	return len(s), nil
}

func (m *mockResponseWriter) WriteHeader(int) {}

func TestParams(t *testing.T) {
	ps := Params{
		Param{"param1", "value1"},
		Param{"param2", "value2"},
		Param{"param3", "value3"},
	}
	for i := range ps {
		if val := ps.ByName(ps[i].Key); val != ps[i].Value {
			t.Errorf("Wrong value for %s: Got %s; Want %s", ps[i].Key, val, ps[i].Value)
		}
	}
	if val := ps.ByName("noKey"); val != "" {
		t.Errorf("Expected empty string for not found key; got: %s", val)
	}
}

func TestRouter(t *testing.T) {
	router := New()

	routed := false
	router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) {
		routed = true
		want := Params{Param{"name", "gopher"}}
		if !reflect.DeepEqual(ps, want) {
			t.Fatalf("wrong wildcard values: want %v, got %v", want, ps)
		}
	})

	w := new(mockResponseWriter)

	req, _ := http.NewRequest("GET", "/user/gopher", nil)
	router.ServeHTTP(w, req)

	if !routed {
		t.Fatal("routing failed")
	}
}

type handlerStruct struct {
	handeled *bool
}

func (h handlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	*h.handeled = true
}

func TestRouterAPI(t *testing.T) {
	var get, head, post, put, patch, delete, handler, handlerFunc bool

	httpHandler := handlerStruct{&handler}

	router := New()
	router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
		get = true
	})
	router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
		head = true
	})
	router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ Params) {
		post = true
	})
	router.PUT("/PUT", func(w http.ResponseWriter, r *http.Request, _ Params) {
		put = true
	})
	router.PATCH("/PATCH", func(w http.ResponseWriter, r *http.Request, _ Params) {
		patch = true
	})
	router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ Params) {
		delete = true
	})
	router.Handler("GET", "/Handler", httpHandler)
	router.HandlerFunc("GET", "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) {
		handlerFunc = true
	})

	w := new(mockResponseWriter)

	r, _ := http.NewRequest("GET", "/GET", nil)
	router.ServeHTTP(w, r)
	if !get {
		t.Error("routing GET failed")
	}

	r, _ = http.NewRequest("HEAD", "/GET", nil)
	router.ServeHTTP(w, r)
	if !head {
		t.Error("routing HEAD failed")
	}

	r, _ = http.NewRequest("POST", "/POST", nil)
	router.ServeHTTP(w, r)
	if !post {
		t.Error("routing POST failed")
	}

	r, _ = http.NewRequest("PUT", "/PUT", nil)
	router.ServeHTTP(w, r)
	if !put {
		t.Error("routing PUT failed")
	}

	r, _ = http.NewRequest("PATCH", "/PATCH", nil)
	router.ServeHTTP(w, r)
	if !patch {
		t.Error("routing PATCH failed")
	}

	r, _ = http.NewRequest("DELETE", "/DELETE", nil)
	router.ServeHTTP(w, r)
	if !delete {
		t.Error("routing DELETE failed")
	}

	r, _ = http.NewRequest("GET", "/Handler", nil)
	router.ServeHTTP(w, r)
	if !handler {
		t.Error("routing Handler failed")
	}

	r, _ = http.NewRequest("GET", "/HandlerFunc", nil)
	router.ServeHTTP(w, r)
	if !handlerFunc {
		t.Error("routing HandlerFunc failed")
	}
}

func TestRouterRoot(t *testing.T) {
	router := New()
	recv := catchPanic(func() {
		router.GET("noSlashRoot", nil)
	})
	if recv == nil {
		t.Fatal("registering path not beginning with '/' did not panic")
	}
}

func TestRouterNotAllowed(t *testing.T) {
	handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}

	router := New()
	router.POST("/path", handlerFunc)

	// Test not allowed
	r, _ := http.NewRequest("GET", "/path", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == http.StatusMethodNotAllowed) {
		t.Errorf("NotAllowed handling route %s failed: Code=%d, Header=%v", w.Code, w.Header())
	}
}

func TestRouterNotFound(t *testing.T) {
	handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}

	router := New()
	router.GET("/path", handlerFunc)
	router.GET("/dir/", handlerFunc)
	router.GET("/", handlerFunc)

	testRoutes := []struct {
		route  string
		code   int
		header string
	}{
		{"/path/", 301, "map[Location:[/path]]"},   // TSR -/
		{"/dir", 301, "map[Location:[/dir/]]"},     // TSR +/
		{"", 301, "map[Location:[/]]"},             // TSR +/
		{"/PATH", 301, "map[Location:[/path]]"},    // Fixed Case
		{"/DIR/", 301, "map[Location:[/dir/]]"},    // Fixed Case
		{"/PATH/", 301, "map[Location:[/path]]"},   // Fixed Case -/
		{"/DIR", 301, "map[Location:[/dir/]]"},     // Fixed Case +/
		{"/../path", 301, "map[Location:[/path]]"}, // CleanPath
		{"/nope", 404, ""},                         // NotFound
	}
	for _, tr := range testRoutes {
		r, _ := http.NewRequest("GET", tr.route, nil)
		w := httptest.NewRecorder()
		router.ServeHTTP(w, r)
		if !(w.Code == tr.code && (w.Code == 404 || fmt.Sprint(w.Header()) == tr.header)) {
			t.Errorf("NotFound handling route %s failed: Code=%d, Header=%v", tr.route, w.Code, w.Header())
		}
	}

	// Test custom not found handler
	var notFound bool
	router.NotFound = func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(404)
		notFound = true
	}
	r, _ := http.NewRequest("GET", "/nope", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 404 && notFound == true) {
		t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
	}

	// Test other method than GET (want 307 instead of 301)
	router.PATCH("/path", handlerFunc)
	r, _ = http.NewRequest("PATCH", "/path/", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 307 && fmt.Sprint(w.Header()) == "map[Location:[/path]]") {
		t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
	}

	// Test special case where no node for the prefix "/" exists
	router = New()
	router.GET("/a", handlerFunc)
	r, _ = http.NewRequest("GET", "/", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 404) {
		t.Errorf("NotFound handling route / failed: Code=%d", w.Code)
	}
}

func TestRouterPanicHandler(t *testing.T) {
	router := New()
	panicHandled := false

	router.PanicHandler = func(rw http.ResponseWriter, r *http.Request, p interface{}) {
		panicHandled = true
	}

	router.Handle("PUT", "/user/:name", func(_ http.ResponseWriter, _ *http.Request, _ Params) {
		panic("oops!")
	})

	w := new(mockResponseWriter)
	req, _ := http.NewRequest("PUT", "/user/gopher", nil)

	defer func() {
		if rcv := recover(); rcv != nil {
			t.Fatal("handling panic failed")
		}
	}()

	router.ServeHTTP(w, req)

	if !panicHandled {
		t.Fatal("simulating failed")
	}
}

func TestRouterLookup(t *testing.T) {
	routed := false
	wantHandle := func(_ http.ResponseWriter, _ *http.Request, _ Params) {
		routed = true
	}
	wantParams := Params{Param{"name", "gopher"}}

	router := New()

	// try empty router first
	handle, _, tsr := router.Lookup("GET", "/nope")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if tsr {
		t.Error("Got wrong TSR recommendation!")
	}

	// insert route and try again
	router.GET("/user/:name", wantHandle)

	handle, params, tsr := router.Lookup("GET", "/user/gopher")
	if handle == nil {
		t.Fatal("Got no handle!")
	} else {
		handle(nil, nil, nil)
		if !routed {
			t.Fatal("Routing failed!")
		}
	}

	if !reflect.DeepEqual(params, wantParams) {
		t.Fatalf("Wrong parameter values: want %v, got %v", wantParams, params)
	}

	handle, _, tsr = router.Lookup("GET", "/user/gopher/")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if !tsr {
		t.Error("Got no TSR recommendation!")
	}

	handle, _, tsr = router.Lookup("GET", "/nope")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if tsr {
		t.Error("Got wrong TSR recommendation!")
	}
}

type mockFileSystem struct {
	opened bool
}

func (mfs *mockFileSystem) Open(name string) (http.File, error) {
	mfs.opened = true
	return nil, errors.New("this is just a mock")
}

func TestRouterServeFiles(t *testing.T) {
	router := New()
	mfs := &mockFileSystem{}

	recv := catchPanic(func() {
		router.ServeFiles("/noFilepath", mfs)
	})
	if recv == nil {
		t.Fatal("registering path not ending with '*filepath' did not panic")
	}

	router.ServeFiles("/*filepath", mfs)
	w := new(mockResponseWriter)
	r, _ := http.NewRequest("GET", "/favicon.ico", nil)
	router.ServeHTTP(w, r)
	if !mfs.opened {
		t.Error("serving file failed")
	}
}
<commit_msg>Fix TestRouterNotAllowed error format<commit_after>// Copyright 2013 Julien Schmidt. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.

package httprouter

import (
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
)

type mockResponseWriter struct{}

func (m *mockResponseWriter) Header() (h http.Header) {
	return http.Header{}
}

func (m *mockResponseWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
	return len(s), nil
}

func (m *mockResponseWriter) WriteHeader(int) {}

func TestParams(t *testing.T) {
	ps := Params{
		Param{"param1", "value1"},
		Param{"param2", "value2"},
		Param{"param3", "value3"},
	}
	for i := range ps {
		if val := ps.ByName(ps[i].Key); val != ps[i].Value {
			t.Errorf("Wrong value for %s: Got %s; Want %s", ps[i].Key, val, ps[i].Value)
		}
	}
	if val := ps.ByName("noKey"); val != "" {
		t.Errorf("Expected empty string for not found key; got: %s", val)
	}
}

func TestRouter(t *testing.T) {
	router := New()

	routed := false
	router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) {
		routed = true
		want := Params{Param{"name", "gopher"}}
		if !reflect.DeepEqual(ps, want) {
			t.Fatalf("wrong wildcard values: want %v, got %v", want, ps)
		}
	})

	w := new(mockResponseWriter)

	req, _ := http.NewRequest("GET", "/user/gopher", nil)
	router.ServeHTTP(w, req)

	if !routed {
		t.Fatal("routing failed")
	}
}

type handlerStruct struct {
	handeled *bool
}

func (h handlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	*h.handeled = true
}

func TestRouterAPI(t *testing.T) {
	var get, head, post, put, patch, delete, handler, handlerFunc bool

	httpHandler := handlerStruct{&handler}

	router := New()
	router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
		get = true
	})
	router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
		head = true
	})
	router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ Params) {
		post = true
	})
	router.PUT("/PUT", func(w http.ResponseWriter, r *http.Request, _ Params) {
		put = true
	})
	router.PATCH("/PATCH", func(w http.ResponseWriter, r *http.Request, _ Params) {
		patch = true
	})
	router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ Params) {
		delete = true
	})
	router.Handler("GET", "/Handler", httpHandler)
	router.HandlerFunc("GET", "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) {
		handlerFunc = true
	})

	w := new(mockResponseWriter)

	r, _ := http.NewRequest("GET", "/GET", nil)
	router.ServeHTTP(w, r)
	if !get {
		t.Error("routing GET failed")
	}

	r, _ = http.NewRequest("HEAD", "/GET", nil)
	router.ServeHTTP(w, r)
	if !head {
		t.Error("routing HEAD failed")
	}

	r, _ = http.NewRequest("POST", "/POST", nil)
	router.ServeHTTP(w, r)
	if !post {
		t.Error("routing POST failed")
	}

	r, _ = http.NewRequest("PUT", "/PUT", nil)
	router.ServeHTTP(w, r)
	if !put {
		t.Error("routing PUT failed")
	}

	r, _ = http.NewRequest("PATCH", "/PATCH", nil)
	router.ServeHTTP(w, r)
	if !patch {
		t.Error("routing PATCH failed")
	}

	r, _ = http.NewRequest("DELETE", "/DELETE", nil)
	router.ServeHTTP(w, r)
	if !delete {
		t.Error("routing DELETE failed")
	}

	r, _ = http.NewRequest("GET", "/Handler", nil)
	router.ServeHTTP(w, r)
	if !handler {
		t.Error("routing Handler failed")
	}

	r, _ = http.NewRequest("GET", "/HandlerFunc", nil)
	router.ServeHTTP(w, r)
	if !handlerFunc {
		t.Error("routing HandlerFunc failed")
	}
}

func TestRouterRoot(t *testing.T) {
	router := New()
	recv := catchPanic(func() {
		router.GET("noSlashRoot", nil)
	})
	if recv == nil {
		t.Fatal("registering path not beginning with '/' did not panic")
	}
}

func TestRouterNotAllowed(t *testing.T) {
	handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}

	router := New()
	router.POST("/path", handlerFunc)

	// Test not allowed
	r, _ := http.NewRequest("GET", "/path", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == http.StatusMethodNotAllowed) {
		t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", w.Code, w.Header())
	}
}

func TestRouterNotFound(t *testing.T) {
	handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}

	router := New()
	router.GET("/path", handlerFunc)
	router.GET("/dir/", handlerFunc)
	router.GET("/", handlerFunc)

	testRoutes := []struct {
		route  string
		code   int
		header string
	}{
		{"/path/", 301, "map[Location:[/path]]"},   // TSR -/
		{"/dir", 301, "map[Location:[/dir/]]"},     // TSR +/
		{"", 301, "map[Location:[/]]"},             // TSR +/
		{"/PATH", 301, "map[Location:[/path]]"},    // Fixed Case
		{"/DIR/", 301, "map[Location:[/dir/]]"},    // Fixed Case
		{"/PATH/", 301, "map[Location:[/path]]"},   // Fixed Case -/
		{"/DIR", 301, "map[Location:[/dir/]]"},     // Fixed Case +/
		{"/../path", 301, "map[Location:[/path]]"}, // CleanPath
		{"/nope", 404, ""},                         // NotFound
	}
	for _, tr := range testRoutes {
		r, _ := http.NewRequest("GET", tr.route, nil)
		w := httptest.NewRecorder()
		router.ServeHTTP(w, r)
		if !(w.Code == tr.code && (w.Code == 404 || fmt.Sprint(w.Header()) == tr.header)) {
			t.Errorf("NotFound handling route %s failed: Code=%d, Header=%v", tr.route, w.Code, w.Header())
		}
	}

	// Test custom not found handler
	var notFound bool
	router.NotFound = func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(404)
		notFound = true
	}
	r, _ := http.NewRequest("GET", "/nope", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 404 && notFound == true) {
		t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
	}

	// Test other method than GET (want 307 instead of 301)
	router.PATCH("/path", handlerFunc)
	r, _ = http.NewRequest("PATCH", "/path/", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 307 && fmt.Sprint(w.Header()) == "map[Location:[/path]]") {
		t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
	}

	// Test special case where no node for the prefix "/" exists
	router = New()
	router.GET("/a", handlerFunc)
	r, _ = http.NewRequest("GET", "/", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 404) {
		t.Errorf("NotFound handling route / failed: Code=%d", w.Code)
	}
}

func TestRouterPanicHandler(t *testing.T) {
	router := New()
	panicHandled := false

	router.PanicHandler = func(rw http.ResponseWriter, r *http.Request, p interface{}) {
		panicHandled = true
	}

	router.Handle("PUT", "/user/:name", func(_ http.ResponseWriter, _ *http.Request, _ Params) {
		panic("oops!")
	})

	w := new(mockResponseWriter)
	req, _ := http.NewRequest("PUT", "/user/gopher", nil)

	defer func() {
		if rcv := recover(); rcv != nil {
			t.Fatal("handling panic failed")
		}
	}()

	router.ServeHTTP(w, req)

	if !panicHandled {
		t.Fatal("simulating failed")
	}
}

func TestRouterLookup(t *testing.T) {
	routed := false
	wantHandle := func(_ http.ResponseWriter, _ *http.Request, _ Params) {
		routed = true
	}
	wantParams := Params{Param{"name", "gopher"}}

	router := New()

	// try empty router first
	handle, _, tsr := router.Lookup("GET", "/nope")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if tsr {
		t.Error("Got wrong TSR recommendation!")
	}

	// insert route and try again
	router.GET("/user/:name", wantHandle)

	handle, params, tsr := router.Lookup("GET", "/user/gopher")
	if handle == nil {
		t.Fatal("Got no handle!")
	} else {
		handle(nil, nil, nil)
		if !routed {
			t.Fatal("Routing failed!")
		}
	}

	if !reflect.DeepEqual(params, wantParams) {
		t.Fatalf("Wrong parameter values: want %v, got %v", wantParams, params)
	}

	handle, _, tsr = router.Lookup("GET", "/user/gopher/")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if !tsr {
		t.Error("Got no TSR recommendation!")
	}

	handle, _, tsr = router.Lookup("GET", "/nope")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if tsr {
		t.Error("Got wrong TSR recommendation!")
	}
}

type mockFileSystem struct {
	opened bool
}

func (mfs *mockFileSystem) Open(name string) (http.File, error) {
	mfs.opened = true
	return nil, errors.New("this is just a mock")
}

func TestRouterServeFiles(t *testing.T) {
	router := New()
	mfs := &mockFileSystem{}

	recv := catchPanic(func() {
		router.ServeFiles("/noFilepath", mfs)
	})
	if recv == nil {
		t.Fatal("registering path not ending with '*filepath' did not panic")
	}

	router.ServeFiles("/*filepath", mfs)
	w := new(mockResponseWriter)
	r, _ := http.NewRequest("GET", "/favicon.ico", nil)
	router.ServeHTTP(w, r)
	if !mfs.opened {
		t.Error("serving file failed")
	}
}
<|endoftext|>


<commit_before>package Golf

import (
	"reflect"
	"testing"
)

func handler(ctx *Context) {}

func TestParsePatternWithOneParam(t *testing.T) {
	cases := []struct {
		in, regex, param string
	}{
		{"/:id/", `^/([\w-%]+)/$`, "id"},
	}

	for _, c := range cases {
		route := newRoute(RouterMethodGet, c.in, handler)
		if route.regex.String() != c.regex {
			t.Errorf("regex of %q  == %q, want %q", c.in, route.regex.String(), c.regex)
		}
		if len(route.params) != 1 {
			t.Errorf("%q is supposed to have 1 parameter", c.in)
		}
		if route.params[0] != "id" {
			t.Errorf("params[0] == %q, want %q", c.in, c.param)
		}
	}
}

func TestParsePatternWithThreeParam(t *testing.T) {
	cases := []struct {
		in, regex string
		params    []string
	}{
		{
			"/:year/:month/:day/",
			`^/([\w-%]+)/([\w-%]+)/([\w-%]+)/$`,
			[]string{"year", "month", "day"},
		},
	}

	for _, c := range cases {
		route := newRoute(RouterMethodGet, c.in, handler)
		if route.regex.String() != c.regex {
			t.Errorf("regex == %q, want %q", c.in, route.regex.String(), c.regex)
		}
		if !reflect.DeepEqual(route.params, c.params) {
			t.Errorf("parameters not match: %v != %v", route.params, c.params)
		}
	}
}

func TestRouterMatch(t *testing.T) {
	router := NewRouter()
	cases := []struct {
		pattern string
		url     string
		params  map[string]string
	}{
		{
			"/:year/:month/:day/",
			"/2015/11/15/",
			map[string]string{"year": "2015", "month": "11", "day": "15"},
		},
		{
			"/user/:id/",
			"/user/foobar/",
			map[string]string{"id": "foobar"},
		},
	}
	for _, c := range cases {
		router.Get(c.pattern, handler)
		params, _ := router.match(c.url, RouterMethodGet)
		if !reflect.DeepEqual(params, c.params) {
			t.Errorf("parameters not match: %v != %v", params, c.params)
		}
	}
}
<commit_msg>[fix] Removed irrelevant arguments in test.<commit_after>package Golf

import (
	"reflect"
	"testing"
)

func handler(ctx *Context) {}

func TestParsePatternWithOneParam(t *testing.T) {
	cases := []struct {
		in, regex, param string
	}{
		{"/:id/", `^/([\w-%]+)/$`, "id"},
	}

	for _, c := range cases {
		route := newRoute(RouterMethodGet, c.in, handler)
		if route.regex.String() != c.regex {
			t.Errorf("regex of %q  == %q, want %q", c.in, route.regex.String(), c.regex)
		}
		if len(route.params) != 1 {
			t.Errorf("%q is supposed to have 1 parameter", c.in)
		}
		if route.params[0] != "id" {
			t.Errorf("params[0] == %q, want %q", c.in, c.param)
		}
	}
}

func TestParsePatternWithThreeParam(t *testing.T) {
	cases := []struct {
		in, regex string
		params    []string
	}{
		{
			"/:year/:month/:day/",
			`^/([\w-%]+)/([\w-%]+)/([\w-%]+)/$`,
			[]string{"year", "month", "day"},
		},
	}

	for _, c := range cases {
		route := newRoute(RouterMethodGet, c.in, handler)
		if route.regex.String() != c.regex {
			t.Errorf("regex == %q, want %q", c.in, route.regex.String())
		}
		if !reflect.DeepEqual(route.params, c.params) {
			t.Errorf("parameters not match: %v != %v", route.params, c.params)
		}
	}
}

func TestRouterMatch(t *testing.T) {
	router := NewRouter()
	cases := []struct {
		pattern string
		url     string
		params  map[string]string
	}{
		{
			"/:year/:month/:day/",
			"/2015/11/15/",
			map[string]string{"year": "2015", "month": "11", "day": "15"},
		},
		{
			"/user/:id/",
			"/user/foobar/",
			map[string]string{"id": "foobar"},
		},
	}
	for _, c := range cases {
		router.Get(c.pattern, handler)
		params, _ := router.match(c.url, RouterMethodGet)
		if !reflect.DeepEqual(params, c.params) {
			t.Errorf("parameters not match: %v != %v", params, c.params)
		}
	}
}
<|endoftext|>


<commit_before>/**
 * (C) Copyright 2014, Deft Labs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dlshared

import (
	"fmt"
	"time"
	"labix.org/v2/mgo"
)

type MongoConnectionType string

const (
	MongosConnectionType = MongoConnectionType("mongos")
	StandaloneConnectionType = MongoConnectionType("standalone")
	ReplicaSetConnectionType = MongoConnectionType("replicaSet")
)

// Create a new Mongo component from a configuration path. The path passed must be in the following format.
//
//    mongodb: {
//        configDb: {
//            mongoUrl: "mongodb://localhost:27017/test",
//            mode: "strong",
//            dialTimeoutInMs: 3000,
//            socketTimeoutInMs: 3000,
//            syncTimeoutInMs: 3000,
//            cursorTimeoutInMs: 30000,
//            type: "standalone",
//        }
//    }
//
//
// The configPath for this component would be "mongodb.configDb". The path can be any arbitrary set of nested
// json documents (json path). If the path is incorrect, the Start() method will panic when called by the kernel.
//
// All of the params above must be present or
// the Start method will panic. If the componentId or configPath param is nil or empty,
// this method will panic.
func NewMongoFromConfigPath(componentId, configPath string) *Mongo {

	if len(componentId) == 0 {
		panic("When calling NewMongoFromConfigPath you must pass in a non-empty componentId param")
	}

	if len(configPath) == 0 {
		panic("When calling NewMongoFromConfigPath you must pass in a non-empty configPath param")
	}

	return &Mongo{ componentId : componentId, configPath : configPath }
}

// Create a new Mongo component. This method will panic if either of the params are nil or len == 0.
func NewMongo(	componentId,
				mongoUrl string,
				connectionType MongoConnectionType,
				mode string,
				dialTimeoutInMs,
				socketTimeoutInMs,
				syncTimeoutInMs,
				cursorTimeoutInMs int) *Mongo {

	if len(componentId) == 0 {
		panic("When calling NewMongo you must pass in a non-empty component id")
	}

	if len(mongoUrl) == 0 {
		panic("When calling NewMongo you must pass in a non-empty Mongo url")
	}

	if len(connectionType) == 0 {
		panic("When calling NewMongo you must pass in a non-empty connection type (standalone | mongos | replicaSet)")
	}

	return &Mongo{
		componentId: componentId,
		mongoUrl: mongoUrl,
		connectionType: MongoConnectionType(connectionType),
		mode: mode,
		dialTimeoutInMs: dialTimeoutInMs,
		socketTimeoutInMs: socketTimeoutInMs,
		syncTimeoutInMs: syncTimeoutInMs,
		cursorTimeoutInMs: cursorTimeoutInMs,
		DefaultSafe: defaultSafe(MongoConnectionType(connectionType)),
	}
}

type Mongo struct {
	kernel *Kernel
	Logger

	configPath string

	componentId string
	mongoUrl string
	mode string
	dialTimeoutInMs int
	socketTimeoutInMs int
	syncTimeoutInMs int
	cursorTimeoutInMs int
	session *mgo.Session
	connectionType MongoConnectionType

	DefaultSafe *mgo.Safe
}

// Returns the collection from the session.
func (self *Mongo) Collection(dbName, collectionName string) *mgo.Collection { return self.Db(dbName).C(collectionName) }

// Returns the database from the session.
func (self *Mongo) Db(name string) *mgo.Database { return self.session.DB(name) }

// Returns the session struct.
func (self *Mongo) Session() *mgo.Session { return self.session }

// Returns a clone of the session struct.
func (self *Mongo) SessionClone() *mgo.Session { return self.session.Clone() }

// Returns a copy of the session struct.
func (self *Mongo) SessionCopy() *mgo.Session { return self.session.Clone() }

// Returns a default safest mode. If this is a mongos or replica set, WMode: "majority" - if this is
// a standalone instance, w: 1
func defaultSafe(connectionType MongoConnectionType) *mgo.Safe {
	switch connectionType {
		case MongosConnectionType: return &mgo.Safe{ WMode: "majority" }
		case ReplicaSetConnectionType: return &mgo.Safe{ WMode: "majority" }
		case StandaloneConnectionType: return &mgo.Safe{ W: 1 }
		default: panic("Unknown connection type: " + connectionType)
	}

	return nil
}

func (self *Mongo) Start(kernel *Kernel) error {
	self.kernel = kernel
	self.Logger = kernel.Logger

	var err error

	// This is a configuration based creation. Load the config data first.
	if len(self.configPath) > 0 {
		self.mongoUrl = self.kernel.Configuration.String(fmt.Sprintf("%s.%s", self.configPath, "mongoUrl"), "")
		self.connectionType = MongoConnectionType(self.kernel.Configuration.String(fmt.Sprintf("%s.%s", self.configPath, "type"), ""))
		self.mode = self.kernel.Configuration.String(fmt.Sprintf("%s.%s", self.configPath, "mode"), "")
		self.dialTimeoutInMs = self.kernel.Configuration.Int(fmt.Sprintf("%s.%s", self.configPath, "dialTimeoutInMs"), -1)
		self.socketTimeoutInMs = self.kernel.Configuration.Int(fmt.Sprintf("%s.%s", self.configPath, "socketTimeoutInMs"), -1)
		self.syncTimeoutInMs = self.kernel.Configuration.Int(fmt.Sprintf("%s.%s", self.configPath, "syncTimeoutInMs"), -1)
		self.cursorTimeoutInMs = self.kernel.Configuration.Int(fmt.Sprintf("%s.%s", self.configPath, "cursorTimeoutInMs"), -1)
	}

	// Validate the params
	if len(self.mongoUrl) == 0 {
		panic(fmt.Sprintf("In Mongo - mongoUrl is not set - componentId: %s", self.componentId))
	}

	if len(self.connectionType) == 0 {
		panic(fmt.Sprintf("In Mongo - type is not set - componentId: %s", self.componentId))
	}

	if self.dialTimeoutInMs < 0 {
		panic(fmt.Sprintf("In Mongo - dialTimeoutInMs is invalid - value: %d - componentId: %s", self.dialTimeoutInMs, self.componentId))
	}

	if self.socketTimeoutInMs < 0 {
		panic(fmt.Sprintf("In Mongo - socketTimeoutInMs is invalid - value: %d - componentId: %s", self.socketTimeoutInMs, self.componentId))
	}

	if self.syncTimeoutInMs < 0 {
		panic(fmt.Sprintf("In Mongo - syncTimeoutInMs is invalid - value: %d - componentId: %s", self.syncTimeoutInMs, self.componentId))
	}

	if self.cursorTimeoutInMs < 0 {
		panic(fmt.Sprintf("In Mongo - cursorTimeoutInMs is invalid - value: %d - componentId: %s", self.cursorTimeoutInMs, self.componentId))
	}

	if len(self.mode) == 0 {
		panic(fmt.Sprintf("In Mongo - mode is invalid - value: %s - componentId: %s", self.mode, self.componentId))
	}

	if self.mode != "strong" && self.mode != "eventual" && self.mode != "montonic" {
		panic(fmt.Sprintf("In Mongo - mode is invalid - value: %s - componentId: %s", self.mode, self.componentId))
	}

	// Create the session.
	if self.session, err = mgo.DialWithTimeout(self.mongoUrl, time.Duration(self.dialTimeoutInMs) * time.Millisecond); err != nil {
		return NewStackError("Unable to init Mongo session - component: %s - mongodbUrl: %s", self.componentId, self.mongoUrl)
	}

	// This is annoying, but mgo defines these constants as the restricted "mode" type.
	switch self.mode {
		case "eventual": self.session.SetMode(mgo.Eventual, true)
		case "monotonic": self.session.SetMode(mgo.Monotonic, true)
		case "strong": self.session.SetMode(mgo.Strong, true)
	}

	self.DefaultSafe = defaultSafe(self.connectionType)

	self.session.SetSocketTimeout(time.Duration(self.socketTimeoutInMs) * time.Millisecond)
	self.session.SetSyncTimeout(time.Duration(self.syncTimeoutInMs) * time.Millisecond)
	self.session.SetSafe(nil)

	return nil
}

// Stop the component. This will close the base session.
func (self *Mongo) Stop(kernel *Kernel) error {

	if self.session != nil {
		self.session.Close()
	}

	return nil
}

<commit_msg>use the new configuration conv method for pulling out items with a path.<commit_after>/**
 * (C) Copyright 2014, Deft Labs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dlshared

import (
	"fmt"
	"time"
	"labix.org/v2/mgo"
)

type MongoConnectionType string

const (
	MongosConnectionType = MongoConnectionType("mongos")
	StandaloneConnectionType = MongoConnectionType("standalone")
	ReplicaSetConnectionType = MongoConnectionType("replicaSet")
)

// Create a new Mongo component from a configuration path. The path passed must be in the following format.
//
//    "mongodb": {
//        "configDb": {
//            "mongoUrl": "mongodb://localhost:27017/test",
//            "mode": "strong",
//            "dialTimeoutInMs": 3000,
//            "socketTimeoutInMs": 3000,
//            "syncTimeoutInMs": 3000,
//            "cursorTimeoutInMs": 30000,
//            "type": "standalone",
//        }
//    }
//
//
// The configPath for this component would be "mongodb.configDb". The path can be any arbitrary set of nested
// json documents (json path). If the path is incorrect, the Start() method will panic when called by the kernel.
//
// All of the params above must be present or
// the Start method will panic. If the componentId or configPath param is nil or empty,
// this method will panic.
func NewMongoFromConfigPath(componentId, configPath string) *Mongo {

	if len(componentId) == 0 {
		panic("When calling NewMongoFromConfigPath you must pass in a non-empty componentId param")
	}

	if len(configPath) == 0 {
		panic("When calling NewMongoFromConfigPath you must pass in a non-empty configPath param")
	}

	return &Mongo{ componentId : componentId, configPath : configPath }
}

// Create a new Mongo component. This method will panic if either of the params are nil or len == 0.
func NewMongo(	componentId,
				mongoUrl string,
				connectionType MongoConnectionType,
				mode string,
				dialTimeoutInMs,
				socketTimeoutInMs,
				syncTimeoutInMs,
				cursorTimeoutInMs int) *Mongo {

	if len(componentId) == 0 {
		panic("When calling NewMongo you must pass in a non-empty component id")
	}

	if len(mongoUrl) == 0 {
		panic("When calling NewMongo you must pass in a non-empty Mongo url")
	}

	if len(connectionType) == 0 {
		panic("When calling NewMongo you must pass in a non-empty connection type (standalone | mongos | replicaSet)")
	}

	return &Mongo{
		componentId: componentId,
		mongoUrl: mongoUrl,
		connectionType: MongoConnectionType(connectionType),
		mode: mode,
		dialTimeoutInMs: dialTimeoutInMs,
		socketTimeoutInMs: socketTimeoutInMs,
		syncTimeoutInMs: syncTimeoutInMs,
		cursorTimeoutInMs: cursorTimeoutInMs,
		DefaultSafe: defaultSafe(MongoConnectionType(connectionType)),
	}
}

type Mongo struct {
	kernel *Kernel
	Logger

	configPath string

	componentId string
	mongoUrl string
	mode string
	dialTimeoutInMs int
	socketTimeoutInMs int
	syncTimeoutInMs int
	cursorTimeoutInMs int
	session *mgo.Session
	connectionType MongoConnectionType

	DefaultSafe *mgo.Safe
}

// Returns the collection from the session.
func (self *Mongo) Collection(dbName, collectionName string) *mgo.Collection { return self.Db(dbName).C(collectionName) }

// Returns the database from the session.
func (self *Mongo) Db(name string) *mgo.Database { return self.session.DB(name) }

// Returns the session struct.
func (self *Mongo) Session() *mgo.Session { return self.session }

// Returns a clone of the session struct.
func (self *Mongo) SessionClone() *mgo.Session { return self.session.Clone() }

// Returns a copy of the session struct.
func (self *Mongo) SessionCopy() *mgo.Session { return self.session.Clone() }

// Returns a default safest mode. If this is a mongos or replica set, WMode: "majority" - if this is
// a standalone instance, w: 1
func defaultSafe(connectionType MongoConnectionType) *mgo.Safe {
	switch connectionType {
		case MongosConnectionType: return &mgo.Safe{ WMode: "majority" }
		case ReplicaSetConnectionType: return &mgo.Safe{ WMode: "majority" }
		case StandaloneConnectionType: return &mgo.Safe{ W: 1 }
		default: panic("Unknown connection type: " + connectionType)
	}

	return nil
}

func (self *Mongo) Start(kernel *Kernel) error {
	self.kernel = kernel
	self.Logger = kernel.Logger

	var err error

	// This is a configuration based creation. Load the config data first.
	if len(self.configPath) > 0 {
		self.mongoUrl = self.kernel.Configuration.StringWithPath(self.configPath, "mongoUrl", "")

		self.connectionType = MongoConnectionType(self.kernel.Configuration.StringWithPath(self.configPath, "type", ""))
		self.mode = self.kernel.Configuration.StringWithPath(self.configPath, "mode", "")
		self.dialTimeoutInMs = self.kernel.Configuration.IntWithPath(self.configPath, "dialTimeoutInMs", -1)
		self.socketTimeoutInMs = self.kernel.Configuration.IntWithPath(self.configPath, "socketTimeoutInMs", -1)
		self.syncTimeoutInMs = self.kernel.Configuration.IntWithPath(self.configPath, "syncTimeoutInMs", -1)
		self.cursorTimeoutInMs = self.kernel.Configuration.IntWithPath(self.configPath, "cursorTimeoutInMs", -1)
	}

	// Validate the params
	if len(self.mongoUrl) == 0 { panic(fmt.Sprintf("In Mongo - mongoUrl is not set - componentId: %s", self.componentId)) }

	if len(self.connectionType) == 0 { panic(fmt.Sprintf("In Mongo - type is not set - componentId: %s", self.componentId)) }

	if self.dialTimeoutInMs < 0 { panic(fmt.Sprintf("In Mongo - dialTimeoutInMs is invalid - value: %d - componentId: %s", self.dialTimeoutInMs, self.componentId)) }

	if self.socketTimeoutInMs < 0 { panic(fmt.Sprintf("In Mongo - socketTimeoutInMs is invalid - value: %d - componentId: %s", self.socketTimeoutInMs, self.componentId)) }

	if self.syncTimeoutInMs < 0 { panic(fmt.Sprintf("In Mongo - syncTimeoutInMs is invalid - value: %d - componentId: %s", self.syncTimeoutInMs, self.componentId)) }

	if self.cursorTimeoutInMs < 0 { panic(fmt.Sprintf("In Mongo - cursorTimeoutInMs is invalid - value: %d - componentId: %s", self.cursorTimeoutInMs, self.componentId)) }

	if len(self.mode) == 0 { panic(fmt.Sprintf("In Mongo - mode is invalid - value: %s - componentId: %s", self.mode, self.componentId)) }

	if self.mode != "strong" && self.mode != "eventual" && self.mode != "montonic" { panic(fmt.Sprintf("In Mongo - mode is invalid - value: %s - componentId: %s", self.mode, self.componentId)) }

	// Create the session.
	if self.session, err = mgo.DialWithTimeout(self.mongoUrl, time.Duration(self.dialTimeoutInMs) * time.Millisecond); err != nil {
		return NewStackError("Unable to init Mongo session - component: %s - mongodbUrl: %s", self.componentId, self.mongoUrl)
	}

	// This is annoying, but mgo defines these constants as the restricted "mode" type.
	switch self.mode {
		case "eventual": self.session.SetMode(mgo.Eventual, true)
		case "monotonic": self.session.SetMode(mgo.Monotonic, true)
		case "strong": self.session.SetMode(mgo.Strong, true)
	}

	self.DefaultSafe = defaultSafe(self.connectionType)

	self.session.SetSocketTimeout(time.Duration(self.socketTimeoutInMs) * time.Millisecond)
	self.session.SetSyncTimeout(time.Duration(self.syncTimeoutInMs) * time.Millisecond)
	self.session.SetSafe(nil)

	return nil
}

// Stop the component. This will close the base session.
func (self *Mongo) Stop(kernel *Kernel) error {

	if self.session != nil {
		self.session.Close()
	}

	return nil
}

<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"gopkg.in/mgo.v2"
  "gopkg.in/mgo.v2/bson"
  "github.com/emicklei/go-restful"
)

type Movie struct {
  ID          bson.ObjectId `bson:"_id,omitempty"`
	Title       string
	Genre       string
	Released    string
	Description string
	Cast        []string
}

type MovieResource struct { /* empty */}

func (MovieResource) coll() *mgo.Collection {
  session, err := mgo.Dial("localhost")
  if err != nil {
    panic(err)
  }
  return session.DB("sleepy-movies").C("movies")
}

func (u MovieResource) Register(container *restful.Container) {
  ws := new(restful.WebService)
  ws.Path("/movies").
    Doc("Manage Movies").
    Consumes(restful.MIME_XML, restful.MIME_JSON).
    Produces(restful.MIME_JSON, restful.MIME_XML)

  ws.Route(ws.GET("/").To(u.findMany).
    Doc("get all movies").
    Operation("findMany").
    Writes([]Movie{}))

  ws.Route(ws.GET("/{movie-id}").To(u.findOne).
    Doc("get a movie").
    Operation("findOne").
    Param(ws.PathParameter("movie-id", "identifier of the movie").DataType("string")).
    Writes(Movie{}))

  // ws.Route(ws.PUT("/{movie-id}").To(u.update).
  //   Doc("update a movie").
  //   Operation("update").
  //   Param(ws.PathParameter("movie-id", "identifier of the movie").DataType("string")).
  //   Returns(409, "duplicate movie-id", nil).
  //   Reads(Movie{}))

  // ws.Route(ws.POST("").To(u.create).
  //   Doc("create a movie").
  //   Operation("create").
  //   Reads(Movie{}))

  // ws.Route(ws.DELETE("/{movie-id}").To(u.remove).
  //   Doc("delete a movie").
  //   Operation("remove").
  //   Param(ws.PathParameter("movie-id", "identifier of the movie").DataType("string")))

  container.Add(ws)
}

func (u MovieResource) findMany(request *restful.Request, response *restful.Response) {
  var movies []Movie
  u.coll().Find(nil).All(&movies)
  response.WriteEntity(movies)
}

// GET http://localhost:8080/movies/1
//
func (u MovieResource) findOne(request *restful.Request, response *restful.Response) {
  var movie Movie
  movieID := bson.ObjectIdHex(request.PathParameter("movie-id"))

  if u.coll().FindId(movieID).One(&movie) != nil {
    response.AddHeader("Content-Type", "text/plain")
    response.WriteErrorString(http.StatusNotFound, "404: Movie could not be found.")
    return
  }
  response.WriteEntity(movie)
}

// // POST http://localhost:8080/movies
// // <User><Name>Melissa</Name></User>
// //
// func (u *MovieResource) create(request *restful.Request, response *restful.Response) {
//   usr := new(User)
//   err := request.ReadEntity(usr)
//   if err != nil {
//     response.AddHeader("Content-Type", "text/plain")
//     response.WriteErrorString(http.StatusInternalServerError, err.Error())
//     return
//   }
//   usr.Id = strconv.Itoa(len(u.users) + 1) // simple id generation
//   u.users[usr.Id] = *usr
//   response.WriteHeader(http.StatusCreated)
//   response.WriteEntity(usr)
// }

// // PUT http://localhost:8080/movies/1
// // <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
// //
// func (u *MovieResource) update(request *restful.Request, response *restful.Response) {
//   usr := new(User)
//   err := request.ReadEntity(&usr)
//   if err != nil {
//     response.AddHeader("Content-Type", "text/plain")
//     response.WriteErrorString(http.StatusInternalServerError, err.Error())
//     return
//   }
//   u.users[usr.Id] = *usr
//   response.WriteEntity(usr)
// }

// // DELETE http://localhost:8080/movies/1
// //
// func (u *MovieResource) remove(request *restful.Request, response *restful.Response) {
//   id := request.PathParameter("movie-id")
//   delete(u.users, id)
// }<commit_msg>adding more bits for movies<commit_after>package main

import (
	"net/http"
	"gopkg.in/mgo.v2"
  "gopkg.in/mgo.v2/bson"
  "github.com/emicklei/go-restful"
)

type Movie struct {
  ID          bson.ObjectId `bson:"_id,omitempty"`
	Title       string
	Genre       string
	Released    string
	Description string
	Cast        []string
}

type MovieResource struct { /* empty */}

func (MovieResource) coll() *mgo.Collection {
  session, err := mgo.Dial("localhost")
  if err != nil {
    panic(err)
  }
  return session.DB("sleepy-movies").C("movies")
}

func (u MovieResource) Register(container *restful.Container) {
  ws := new(restful.WebService)
  ws.Path("/movies").
    Doc("Manage Movies").
    Consumes(restful.MIME_XML, restful.MIME_JSON).
    Produces(restful.MIME_JSON, restful.MIME_XML)

  ws.Route(ws.GET("/").To(u.findMany).
    Doc("get all movies").
    Operation("findMany").
    Writes([]Movie{}))

  ws.Route(ws.GET("/{movie-id}").To(u.findOne).
    Doc("get a movie").
    Operation("findOne").
    Param(ws.PathParameter("movie-id", "identifier of the movie").DataType("string")).
    Writes(Movie{}))

  ws.Route(ws.PUT("/{movie-id}").To(u.update).
    Doc("update a movie").
    Operation("update").
    Param(ws.PathParameter("movie-id", "identifier of the movie").DataType("string")).
    Returns(409, "duplicate movie-id", nil).
    Reads(Movie{}))

  ws.Route(ws.POST("/").To(u.create).
    Doc("create a movie").
    Operation("create").
    Reads(Movie{}))

  ws.Route(ws.DELETE("/{movie-id}").To(u.delete).
    Doc("delete a movie").
    Operation("delete").
    Param(ws.PathParameter("movie-id", "identifier of the movie").DataType("string")))

  container.Add(ws)
}

func (u MovieResource) findMany(request *restful.Request, response *restful.Response) {
  var movies []Movie
  u.coll().Find(nil).All(&movies)
  response.WriteEntity(movies)
}

// GET http://localhost:8080/movies/1
//
func (u MovieResource) findOne(request *restful.Request, response *restful.Response) {
  var movie Movie
  movieID := bson.ObjectIdHex(request.PathParameter("movie-id"))

  if u.coll().FindId(movieID).One(&movie) != nil {
    response.AddHeader("Content-Type", "text/plain")
    response.WriteErrorString(http.StatusNotFound, "404: Movie could not be found.")
    return
  }
  response.WriteEntity(movie)
}

// POST http://localhost:8080/movies
// <User><Name>Melissa</Name></User>
//
func (u *MovieResource) create(request *restful.Request, response *restful.Response) {
  movie := new(Movie)
  if err := request.ReadEntity(movie); err != nil {
    response.AddHeader("Content-Type", "text/plain")
    response.WriteErrorString(http.StatusInternalServerError, err.Error())
    return
  }
  if !movie.ID.Valid() {
    movie.ID = bson.NewObjectId()
  }
  if err := u.coll().Insert(movie); err != nil {
    response.AddHeader("Content-Type", "text/plain")
    response.WriteErrorString(http.StatusInternalServerError, err.Error())
    return
  }

  response.WriteHeader(http.StatusCreated)
  response.WriteEntity(movie)
}

// PUT http://localhost:8080/movies/1
// <User><Id>1</Id><Name>Melissa Raspberry</Name></User>
//
func (u *MovieResource) update(request *restful.Request, response *restful.Response) {
  movie := new(Movie)
  if err := request.ReadEntity(movie); err != nil {
    response.AddHeader("Content-Type", "text/plain")
    response.WriteErrorString(http.StatusInternalServerError, err.Error())
    return
  }
  if !movie.ID.Valid() {
    movie.ID = bson.ObjectIdHex(request.PathParameter("movie-id"))
  }
  if _, err := u.coll().UpsertId(movie.ID, movie); err != nil {
    response.AddHeader("Content-Type", "text/plain")
    response.WriteErrorString(http.StatusInternalServerError, err.Error())
    return
  }

  response.WriteHeader(http.StatusCreated)
  response.WriteEntity(movie)
}

// DELETE http://localhost:8080/movies/1
//
func (u *MovieResource) delete(request *restful.Request, response *restful.Response) {
  if err := u.coll().RemoveId(bson.ObjectIdHex(request.PathParameter("movie-id"))); err != nil {
    response.AddHeader("Content-Type", "text/plain")
    response.WriteErrorString(http.StatusInternalServerError, err.Error())
    return
  }
}<|endoftext|>


<commit_before>// IronMQ (elastic message queue) client library
package mq

import (
	"errors"
	"time"

	"github.com/iron-io/iron_go/api"
	"github.com/iron-io/iron_go/config"
)

type Queue struct {
	Settings config.Settings
	Name     string
}

type QueueSubscriber struct {
	URL string `json:"url"`
}

type QueueInfo struct {
	Id              string            `json:"id,omitempty"`
	Name            string            `json:"name,omitempty"`
	Size            int               `json:"size,omitempty"`
	Reserved        int               `json:"reserved,omitempty"`
	TotalMessages   int               `json:"total_messages,omitempty"`
	MaxReqPerMinute int               `json:"max_req_per_minute,omitempty"`
	Subscribers     []QueueSubscriber `json:"subscribers,omitempty"`
	PushType        string            `json:"push_type,omitempty"`
}

type Message struct {
	Id   string `json:"id,omitempty"`
	Body string `json:"body"`
	// Timeout is the amount of time in seconds allowed for processing the
	// message.
	Timeout int64 `json:"timeout,omitempty"`
	// Delay is the amount of time in seconds to wait before adding the message
	// to the queue.
	Delay int64 `json:"delay,omitempty"`
	q     Queue
}

func New(queueName string) *Queue {
	return &Queue{Settings: config.Config("iron_mq"), Name: queueName}
}

func (q Queue) queues(s ...string) *api.URL { return api.Action(q.Settings, "queues", s...) }

func (q Queue) ListQueues(page, perPage int) (queues []Queue, err error) {
	out := []struct {
		Id         string
		Project_id string
		Name       string
	}{}

	err = q.queues().
		QueryAdd("page", "%d", page).
		QueryAdd("per_page", "%d", perPage).
		Req("GET", nil, &out)
	if err != nil {
		return
	}

	queues = make([]Queue, 0, len(out))
	for _, item := range out {
		queues = append(queues, Queue{
			Settings: q.Settings,
			Name:     item.Name,
		})
	}

	return
}

func (q Queue) Info() (QueueInfo, error) {
	qi := QueueInfo{}
	err := q.queues(q.Name).Req("GET", nil, &qi)
	return qi, err
}

func (q Queue) Subscribe(pushType string, subscribers ...string) (err error) {
	in := QueueInfo{
		PushType:    pushType,
		Subscribers: make([]QueueSubscriber, len(subscribers)),
	}
	for i, subscriber := range subscribers {
		in.Subscribers[i].URL = subscriber
	}
	return q.queues(q.Name).Req("POST", &in, nil)
}

func (q Queue) PushString(body string) (id string, err error) {
	ids, err := q.PushStrings(body)
	if err != nil {
		return
	}
	return ids[0], nil
}

// Push adds one or more messages to the end of the queue using IronMQ's defaults:
//	timeout - 60 seconds
//	delay - none
//
// Identical to PushMessages with Message{Timeout: 60, Delay: 0}
func (q Queue) PushStrings(bodies ...string) (ids []string, err error) {
	msgs := make([]*Message, 0, len(bodies))
	for _, body := range bodies {
		msgs = append(msgs, &Message{Body: body})
	}

	return q.PushMessages(msgs...)
}

func (q Queue) PushMessage(msg *Message) (id string, err error) {
	ids, err := q.PushMessages(msg)
	if err != nil {
		return
	}
	return ids[0], nil
}

func (q Queue) PushMessages(msgs ...*Message) (ids []string, err error) {
	in := struct {
		Messages []*Message `json:"messages"`
	}{Messages: msgs}

	out := struct {
		IDs []string `json:"ids"`
		Msg string   `json:"msg"`
	}{}

	err = q.queues(q.Name, "messages").Req("POST", &in, &out)
	return out.IDs, err
}

// Get reserves a message from the queue.
// The message will not be deleted, but will be reserved until the timeout
// expires. If the timeout expires before the message is deleted, the message
// will be placed back onto the queue.
// As a result, be sure to Delete a message after you're done with it.
func (q Queue) Get() (msg *Message, err error) {
	msgs, err := q.GetN(1)
	if err != nil {
		return
	}

	if len(msgs) > 0 {
		msg = msgs[0]
	} else {
		err = errors.New("Couldn't get a single message")
	}

	return
}

// get N messages
func (q Queue) GetN(n int) (msgs []*Message, err error) {
	out := struct {
		Messages []*Message `json:"messages"`
	}{}

	err = q.queues(q.Name, "messages").
		QueryAdd("n", "%d", n).
		Req("GET", nil, &out)
	if err != nil {
		return
	}

	for _, msg := range out.Messages {
		msg.q = q
	}

	return out.Messages, nil
}

// Delete all messages in the queue
func (q Queue) Clear() (err error) {
	return q.queues(q.Name, "clear").Req("POST", nil, nil)
}

// Delete message from queue
func (q Queue) DeleteMessage(msgId string) (err error) {
	return q.queues(q.Name, "messages", msgId).Req("DELETE", nil, nil)
}

// Reset timeout of message to keep it reserved
func (q Queue) TouchMessage(msgId string) (err error) {
	return q.queues(q.Name, "messages", msgId, "touch").Req("POST", nil, nil)
}

// Put message back in the queue, message will be available after +delay+ seconds.
func (q Queue) ReleaseMessage(msgId string, delay int64) (err error) {
	in := struct {
		Delay int64 `json:"messages"`
	}{Delay: delay}
	return q.queues(q.Name, "messages", msgId, "release").Req("POST", &in, nil)
}

type PushStatus struct {
	Retried    int    `json:"retried"`
	StatusCode int    `json:"status_code"`
	Status     string `json:"status"`
}

type Subscriber struct {
	Body          string            `json:"body"`
	CorrelationId string            `json:"correlation_id"`
	Id            string            `json:"id"`
	PushStatus    PushStatus        `json:"push_status"`
	PushType      string            `json:"push_type"`
	URLs          []QueueSubscriber `json:"urls"`
}

func (q Queue) MessageSubscribers(msgId string) ([]*Subscriber, error) {
	out := struct {
		Subscribers []*Subscriber `json:"subscribers"`
	}{}
	err := q.queues(q.Name, "messages", msgId, "subscribers").Req("GET", nil, &out)
	return out.Subscribers, err
}

func (q Queue) MessageSubscribersPollN(msgId string, n int) ([]*Subscriber, error) {
	subs, err := q.MessageSubscribers(msgId)
	for {
		time.Sleep(100 * time.Millisecond)
		subs, err = q.MessageSubscribers(msgId)
		if err != nil {
			return subs, err
		}
		if len(subs) >= n && actualPushStatus(subs) {
			return subs, nil
		}
	}
	return subs, err
}

func actualPushStatus(subs []*Subscriber) bool {
	for _, sub := range subs {
		ps := sub.PushStatus
		if ps.StatusCode == -1 {
			return false
		}
	}

	return true
}

// Delete message from queue
func (m Message) Delete() (err error) {
	return m.q.DeleteMessage(m.Id)
}

// Reset timeout of message to keep it reserved
func (m Message) Touch() (err error) {
	return m.q.TouchMessage(m.Id)
}

// Put message back in the queue, message will be available after +delay+ seconds.
func (m Message) Release(delay int64) (err error) {
	return m.q.ReleaseMessage(m.Id, delay)
}

func (m Message) Subscribers() (interface{}, error) {
	return m.q.MessageSubscribers(m.Id)
}
<commit_msg>adjust for new api<commit_after>// IronMQ (elastic message queue) client library
package mq

import (
	"errors"
	"log"
	"time"

	"github.com/iron-io/iron_go/api"
	"github.com/iron-io/iron_go/config"
)

type Queue struct {
	Settings config.Settings
	Name     string
}

type QueueSubscriber struct {
	URL string `json:"url"`
}

type QueueInfo struct {
	Id              string            `json:"id,omitempty"`
	Name            string            `json:"name,omitempty"`
	Size            int               `json:"size,omitempty"`
	Reserved        int               `json:"reserved,omitempty"`
	TotalMessages   int               `json:"total_messages,omitempty"`
	MaxReqPerMinute int               `json:"max_req_per_minute,omitempty"`
	Subscribers     []QueueSubscriber `json:"subscribers,omitempty"`
	PushType        string            `json:"push_type,omitempty"`
}

type Message struct {
	Id   string `json:"id,omitempty"`
	Body string `json:"body"`
	// Timeout is the amount of time in seconds allowed for processing the
	// message.
	Timeout int64 `json:"timeout,omitempty"`
	// Delay is the amount of time in seconds to wait before adding the message
	// to the queue.
	Delay int64 `json:"delay,omitempty"`
	q     Queue
}

type PushStatus struct {
	Retried    int    `json:"retried"`
	StatusCode int    `json:"status_code"`
	Status     string `json:"status"`
}

type Subscriber struct {
	Retried    int               `json:"retried"`
	StatusCode int               `json:"status_code"`
	Status     string            `json:"status"`
	URLs       []QueueSubscriber `json:"urls"`
}

func New(queueName string) *Queue {
	return &Queue{Settings: config.Config("iron_mq"), Name: queueName}
}

func (q Queue) queues(s ...string) *api.URL { return api.Action(q.Settings, "queues", s...) }

func (q Queue) ListQueues(page, perPage int) (queues []Queue, err error) {
	out := []struct {
		Id         string
		Project_id string
		Name       string
	}{}

	err = q.queues().
		QueryAdd("page", "%d", page).
		QueryAdd("per_page", "%d", perPage).
		Req("GET", nil, &out)
	if err != nil {
		return
	}

	queues = make([]Queue, 0, len(out))
	for _, item := range out {
		queues = append(queues, Queue{
			Settings: q.Settings,
			Name:     item.Name,
		})
	}

	return
}

func (q Queue) Info() (QueueInfo, error) {
	qi := QueueInfo{}
	err := q.queues(q.Name).Req("GET", nil, &qi)
	return qi, err
}

func (q Queue) Subscribe(pushType string, subscribers ...string) (err error) {
	in := QueueInfo{
		PushType:    pushType,
		Subscribers: make([]QueueSubscriber, len(subscribers)),
	}
	for i, subscriber := range subscribers {
		in.Subscribers[i].URL = subscriber
	}
	return q.queues(q.Name).Req("POST", &in, nil)
}

func (q Queue) PushString(body string) (id string, err error) {
	ids, err := q.PushStrings(body)
	if err != nil {
		return
	}
	return ids[0], nil
}

// Push adds one or more messages to the end of the queue using IronMQ's defaults:
//	timeout - 60 seconds
//	delay - none
//
// Identical to PushMessages with Message{Timeout: 60, Delay: 0}
func (q Queue) PushStrings(bodies ...string) (ids []string, err error) {
	msgs := make([]*Message, 0, len(bodies))
	for _, body := range bodies {
		msgs = append(msgs, &Message{Body: body})
	}

	return q.PushMessages(msgs...)
}

func (q Queue) PushMessage(msg *Message) (id string, err error) {
	ids, err := q.PushMessages(msg)
	if err != nil {
		return
	}
	return ids[0], nil
}

func (q Queue) PushMessages(msgs ...*Message) (ids []string, err error) {
	in := struct {
		Messages []*Message `json:"messages"`
	}{Messages: msgs}

	out := struct {
		IDs []string `json:"ids"`
		Msg string   `json:"msg"`
	}{}

	err = q.queues(q.Name, "messages").Req("POST", &in, &out)
	return out.IDs, err
}

// Get reserves a message from the queue.
// The message will not be deleted, but will be reserved until the timeout
// expires. If the timeout expires before the message is deleted, the message
// will be placed back onto the queue.
// As a result, be sure to Delete a message after you're done with it.
func (q Queue) Get() (msg *Message, err error) {
	msgs, err := q.GetN(1)
	if err != nil {
		return
	}

	if len(msgs) > 0 {
		msg = msgs[0]
	} else {
		err = errors.New("Couldn't get a single message")
	}

	return
}

// get N messages
func (q Queue) GetN(n int) (msgs []*Message, err error) {
	out := struct {
		Messages []*Message `json:"messages"`
	}{}

	err = q.queues(q.Name, "messages").
		QueryAdd("n", "%d", n).
		Req("GET", nil, &out)
	if err != nil {
		return
	}

	for _, msg := range out.Messages {
		msg.q = q
	}

	return out.Messages, nil
}

// Delete all messages in the queue
func (q Queue) Clear() (err error) {
	return q.queues(q.Name, "clear").Req("POST", nil, nil)
}

// Delete message from queue
func (q Queue) DeleteMessage(msgId string) (err error) {
	return q.queues(q.Name, "messages", msgId).Req("DELETE", nil, nil)
}

// Reset timeout of message to keep it reserved
func (q Queue) TouchMessage(msgId string) (err error) {
	return q.queues(q.Name, "messages", msgId, "touch").Req("POST", nil, nil)
}

// Put message back in the queue, message will be available after +delay+ seconds.
func (q Queue) ReleaseMessage(msgId string, delay int64) (err error) {
	in := struct {
		Delay int64 `json:"messages"`
	}{Delay: delay}
	return q.queues(q.Name, "messages", msgId, "release").Req("POST", &in, nil)
}

func (q Queue) MessageSubscribers(msgId string) ([]*Subscriber, error) {
	out := struct {
		Subscribers []*Subscriber `json:"subscribers"`
	}{}
	err := q.queues(q.Name, "messages", msgId, "subscribers").Req("GET", nil, &out)
	return out.Subscribers, err
}

func (q Queue) MessageSubscribersPollN(msgId string, n int) ([]*Subscriber, error) {
	subs, err := q.MessageSubscribers(msgId)
	for {
		time.Sleep(100 * time.Millisecond)
		subs, err = q.MessageSubscribers(msgId)
		if err != nil {
			return subs, err
		}
		if len(subs) >= n && actualPushStatus(subs) {
			return subs, nil
		}
	}
	return subs, err
}

func actualPushStatus(subs []*Subscriber) bool {
	for _, sub := range subs {
		if sub.Status == "queued" {
			return false
		}
	}

	return true
}

// Delete message from queue
func (m Message) Delete() (err error) {
	return m.q.DeleteMessage(m.Id)
}

// Reset timeout of message to keep it reserved
func (m Message) Touch() (err error) {
	return m.q.TouchMessage(m.Id)
}

// Put message back in the queue, message will be available after +delay+ seconds.
func (m Message) Release(delay int64) (err error) {
	return m.q.ReleaseMessage(m.Id, delay)
}

func (m Message) Subscribers() (interface{}, error) {
	return m.q.MessageSubscribers(m.Id)
}
<|endoftext|>


<commit_before>package config

import (
	"path"

	cmn "github.com/tendermint/tmlibs/common"
)

/****** these are for production settings ***********/
func EnsureRoot(rootDir string, network string) {
	cmn.EnsureDir(rootDir, 0700)
	cmn.EnsureDir(rootDir+"/data", 0700)

	configFilePath := path.Join(rootDir, "config.toml")

	// Write default config file if missing.
	if !cmn.FileExists(configFilePath) {
		cmn.MustWriteFile(configFilePath, []byte(selectNetwork(network)), 0644)
	}
}

var defaultConfigTmpl = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
fast_sync = true
db_backend = "leveldb"
api_addr = "0.0.0.0:9888"
`

var mainNetConfigTmpl = `chain_id = "mainnet"
[p2p]
laddr = "tcp://0.0.0.0:46656"
seeds = ""
`

var testNetConfigTmpl = `chain_id = "testnet"
[p2p]
laddr = "tcp://0.0.0.0:46656"
seeds = "139.162.105.40:46656,139.162.88.74:46656,47.96.42.1:46656,45.79.213.28:46656,212.111.41.245:46656"
`

// Select network seeds to merge a new string.
func selectNetwork(network string) string {
	if network == "testnet" {
		return defaultConfigTmpl + testNetConfigTmpl
	} else {
		return defaultConfigTmpl + mainNetConfigTmpl
	}
}
<commit_msg>Change testnet seed peer (#602)<commit_after>package config

import (
	"path"

	cmn "github.com/tendermint/tmlibs/common"
)

/****** these are for production settings ***********/
func EnsureRoot(rootDir string, network string) {
	cmn.EnsureDir(rootDir, 0700)
	cmn.EnsureDir(rootDir+"/data", 0700)

	configFilePath := path.Join(rootDir, "config.toml")

	// Write default config file if missing.
	if !cmn.FileExists(configFilePath) {
		cmn.MustWriteFile(configFilePath, []byte(selectNetwork(network)), 0644)
	}
}

var defaultConfigTmpl = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
fast_sync = true
db_backend = "leveldb"
api_addr = "0.0.0.0:9888"
`

var mainNetConfigTmpl = `chain_id = "mainnet"
[p2p]
laddr = "tcp://0.0.0.0:46656"
seeds = ""
`

var testNetConfigTmpl = `chain_id = "testnet"
[p2p]
laddr = "tcp://0.0.0.0:46656"
seeds = "172.104.224.219:46656,198.74.61.131:46656,212.111.41.245:46656"
`

// Select network seeds to merge a new string.
func selectNetwork(network string) string {
	if network == "testnet" {
		return defaultConfigTmpl + testNetConfigTmpl
	} else {
		return defaultConfigTmpl + mainNetConfigTmpl
	}
}
<|endoftext|>


<commit_before><commit_msg>Use template to generate git hook script<commit_after><|endoftext|>


<commit_before>package twitter

import "time"

// Tweet represents a Twitter tweet object.
type Tweet struct {
	Contributor          []Contributor          `json:"contributors"`
	Coordinates          *Coordinates           `json:"coordinates"`
	CreatedAt            string                 `json:"created_at"`
	Entities             Entities               `json:"entities"`
	ExtendedEntities     ExtendedEntities       `json:"extended_entities"`
	FavoriteCount        int                    `json:"favorite_count"`
	Favorited            bool                   `json:"favorited"`
	FilterLevel          string                 `json:"filter_level"`
	ID                   int64                  `json:"id"`
	IDStr                string                 `json:"id_str"`
	InReplyToScreenName  string                 `json:"in_reply_to_screen_name"`
	InReplyToStatusID    int64                  `json:"in_reply_to_status_id"`
	InReplyToStatusIDStr string                 `json:"in_reply_to_status_id_str"`
	InReplyToUserID      int64                  `json:"in_reply_to_user_id"`
	InReplyToUserIDStr   string                 `json:"in_reply_to_user_id_str"`
	Lang                 string                 `json:"lang"`
	Place                *Place                 `json:"place"`
	PossiblySensitive    bool                   `json:"possibly_sensitive"`
	QuotedStatusID       int64                  `json:"quoted_status_id"`
	QuotedStatusIDStr    string                 `json:"quoted_status_id_str"`
	QuotedStatus         *Tweet                 `json:"quoted_status"`
	RetweetCount         int                    `json:"retweet_count"`
	Retweeted            bool                   `json:"retweeted"`
	RetweetedStatus      *Tweet                 `json:"retweeted_status"`
	Scopes               map[string]interface{} `json:"scopes"`
	Source               string                 `json:"source"`
	Text                 string                 `json:"text"`
	Truncated            bool                   `json:"truncated"`
	User                 User                   `json:"user"`
	WithheldCopyright    bool                   `json:"withheld_copyright"`
	WithheldInCountries  []string               `json:"withheld_in_countries"`
	WithheldScope        string                 `json:"withheld_scope"`
}

// Coordinates represents the geographic location of a Tweet as reported by the
// user or client application. The coordinates array is formatted as geoJSON
// (longitude first, then latitude).
type Coordinates struct {
	Coordinates [2]float64 `json:"coordinates"`
	Type        string     `json:"type"`
}

// DirectMessage ...
type DirectMessage struct {
	CreatedAt           string   `json:"created_at"`
	Entities            Entities `json:"entities"`
	ID                  int64    `json:"id"`
	IDStr               string   `json:"id_str"`
	Recipient           User     `json:"recipient"`
	RecipientID         int64    `json:"recipient_id"`
	RecipientScreenName string   `json:"recipient_screen_name"`
	Sender              User     `json:"sender"`
	SenderID            int64    `json:"sender_id"`
	SenderScreenName    string   `json:"sender_screen_name"`
	Text                string   `json:"text"`
}

// ExtendedEntities provides metadata about the media entities present.
type ExtendedEntities struct {
	Media []MediaEntity `json:"media"`
}

// Entities provides metadata and additonal contextual information about twitter
// content.
type Entities struct {
	Hashtags     []HashtagEntity     `json:"hashtags"`
	Media        []MediaEntity       `json:"media"`
	URL          URLEntities         `json:"url"`
	URLs         []URLEntity         `json:"urls"`
	UserMentions []UserMentionEntity `json:"user_mentions"`
}

// HashtagEntity represents hashtags which have been parsed out of the Tweet
// text.
type HashtagEntity struct {
	Indices []int  `json:"indices"`
	Text    string `json:"text"`
}

// MediaEntity represents media elements uploaded with a Tweet.
type MediaEntity struct {
	DisplayURL        string     `json:"display_url"`
	ExpandedURL       string     `json:"expanded_url"`
	ID                int64      `json:"id"`
	IDStr             string     `json:"id_str"`
	Indices           []int      `json:"indices"`
	MediaURL          string     `json:"media_url"`
	MediaURLHTTPS     string     `json:"media_url_https"`
	Sizes             MediaSizes `json:"sizes"`
	SourceStatusID    int64      `json:"source_status_id"`
	SourceStatusIDStr string     `json:"source_status_id_str"`
	Type              string     `json:"type"`
	URL               string     `json:"url"`
	VideoInfo         VideoInfo  `json:"video_info"`
}

// VideoInfo represents information specific to videos.
type VideoInfo struct {
	AspectRatio    []int          `json:"aspect_ratio"`
	DurationMillis int            `json:"duration_millis"`
	Variants       []VideoVariant `json:"variants"`
}

// VideoVariant represents video info for a single bitrate.
type VideoVariant struct {
	Bitrate     int    `json:"bitrate"`
	ContentType string `json:"content_type"`
	URL         string `json:"url"`
}

// MediaSize represents the height, width, and resize string of a media element.
type MediaSize struct {
	Height int    `json:"h"`
	Resize string `json:"resize"`
	Width  int    `json:"w"`
}

// MediaSizes represents a range of media sizes.
type MediaSizes struct {
	Large  MediaSize `json:"large"`
	Medium MediaSize `json:"medium"`
	Small  MediaSize `json:"small"`
	Thumb  MediaSize `json:"thumb"`
}

// UserMentionEntity represents other Twitter users mentioned in the text of a
// tweet.
type UserMentionEntity struct {
	ID         int64  `json:"id"`
	IDStr      string `json:"id_str"`
	Indices    []int  `json:"indices"`
	Name       string `json:"name"`
	ScreenName string `json:"screen_name"`
}

// BoundingBox represents a bounding box of coordinates which encloses a Place.
type BoundingBox struct {
	Coordinates [][][2]float64 `json:"coordinates"`
	Type        string         `json:"type"`
}

// Place represents a specific named location with corresponding geo
// coordinates.
type Place struct {
	Attributes  map[string]string `json:"attributes"`
	BoundingBox BoundingBox       `json:"bounding_box"`
	Country     string            `json:"country"`
	CountryCode string            `json:"country_code"`
	FullName    string            `json:"full_name"`
	ID          string            `json:"id"`
	Name        string            `json:"name"`
	PlaceType   string            `json:"place_type"`
	URL         string            `json:"url"`
}

// Contributor represents a user who contributed to the authorship of a tweet.
type Contributor struct {
	ID         int64  `json:"id"`
	IDStr      string `json:"id_str"`
	ScreenName string `json:"screen_name"`
}

// CreatedAtTime returns a time.Time version of the created date.
func (t *Tweet) CreatedAtTime() (time.Time, error) {
	return time.Parse(time.RubyDate, t.CreatedAt)
}

// URLEntities represents a list of url entities.
type URLEntities struct {
	URLs []URLEntity `json:"urls"`
}

// URLEntity represents URLs included in the text of a Tweet or within textual
// fields of a user object.
type URLEntity struct {
	DisplayURL  string `json:"display_url"`
	ExpandedURL string `json:"expanded_url"`
	Indices     []int  `json:"indices"`
	URL         string `json:"url"`
}

// User represents a twitter user object, as received using the twitter REST
// API.
type User struct {
	ContributorsEnabled            bool     `json:"contributors_enabled"`
	CreatedAt                      string   `json:"created_at"`
	DefaultProfile                 bool     `json:"default_profile"`
	DefaultProfileImage            bool     `json:"default_profile_image"`
	Description                    string   `json:"description"`
	Entities                       Entities `json:"entities"`
	FavouritesCount                int      `json:"favourites_count"`
	FollowRequestSent              bool     `json:"follow_request_sent"`
	Following                      bool     `json:"following"`
	FollowersCount                 int      `json:"followers_count"`
	FriendsCount                   int      `json:"friends_count"`
	GeoEnabled                     bool     `json:"geo_enabled"`
	ID                             int64    `json:"id"`
	IDStr                          string   `json:"id_str"`
	IsTranslator                   bool     `json:"is_translator"`
	Lang                           string   `json:"lang"`
	ListedCount                    int      `json:"listed_count"`
	Location                       string   `json:"location"`
	Name                           string   `json:"name"`
	Notifications                  bool     `json:"notifications"`
	ProfileBackgroundColor         string   `json:"profile_background_color"`
	ProfileBackgroundImageURL      string   `json:"profile_background_image_url"`
	ProfileBackgroundImageURLHTTPS string   `json:"profile_background_image_url_https"`
	ProfileBackgroundTile          bool     `json:"profile_background_tile"`
	ProfileBannerURL               string   `json:"profile_banner_url"`
	ProfileImageURL                string   `json:"profile_image_url"`
	ProfileImageURLHTTPS           string   `json:"profile_image_url_https"`
	ProfileLinkColor               string   `json:"profile_link_color"`
	ProfileSidebarBorderColor      string   `json:"profile_sidebar_border_color"`
	ProfileSidebarFillColor        string   `json:"profile_sidebar_fill_color"`
	ProfileTextColor               string   `json:"profile_text_color"`
	ProfileUseBackgroundImage      bool     `json:"profile_use_background_image"`
	Protected                      bool     `json:"protected"`
	ScreenName                     string   `json:"screen_name"`
	ShowAllInlineMedia             bool     `json:"show_all_inline_media"`
	Status                         *Tweet   `json:"status"` // can be null
	StatusesCount                  int      `json:"statuses_count"`
	TimeZone                       string   `json:"time_zone"`
	URL                            string   `json:"url"`
	UTCOffset                      int      `json:"utc_offset"`
	Verified                       bool     `json:"verified"`
	WithheldInCountries            string   `json:"withheld_in_countries"`
	WithheldScope                  string   `json:"withheld_scope"`
}

// Configuration represents the configuration object received from Twitter help/configuration endpoint
type Configuration struct {
	CharactersReservedPerMedia int                  `json:"characters_reserved_per_media"`
	DMTextCharacterLimit       int                  `json:"dm_text_character_limit"`
	MaxMediaPerUpload          int                  `json:"max_media_per_upload"`
	PhotoSizeLimit             int                  `json:"photo_size_limit"`
	PhotoSizes                 map[string]PhotoSize `json:"photo_sizes"`
	ShortURLLength             int                  `json:"short_url_length"`
	ShortURLLengthHTTPS        int                  `json:"short_url_length_https"`
	NonUsernamePaths           []string             `json:"non_username_paths"`
}

// PhotoSize represents the photo size object iside configuration objectt
type PhotoSize struct {
	H      int    `json:"h"`
	Resize string `json:"resize"`
	W      int    `json:"w"`
}

// Language represents the language obejct received from Twitter help/langauges
type Language struct {
	Code   string `json:"code"`
	Status string `json:"status"`
	Name   string `json:"name"`
}

// OEmbed represents the response body for an oembed request.
type OEmbed struct {
	CacheAge     string `json:"cache_age"`
	URL          string `json:"url"`
	ProviderURL  string `json:"provider_url"`
	ProviderName string `json:"provider_name"`
	AuthorName   string `json:"author_name"`
	Version      string `json:"version"`
	AuthorURL    string `json:"author_url"`
	Type         string `json:"type"`
	HTML         string `json:"html"`
	Height       *int   `json:"height"`
	Width        *int   `json:"width"`
}

// IDs represents a paginated list of string IDs.
type IDs struct {
	IDs               []string `json:"ids"`
	PreviousCursor    int64    `json:"previous_cursor"`
	PreviousCursotStr string   `json:"previous_cursor_str"`
	NextCursor        int64    `json:"next_cursor"`
	NextCursorStr     string   `json:"next_cursor_str"`
}

// RelationshipTarget represents a Twitter user's relationship with a source
type RelationshipTarget struct {
	IDStr      string `json:"id_str"`
	ID         int64  `json:"id"`
	ScreenName string `json:"screen_name"`
	Following  bool   `json:"following"`
	FollowedBy bool   `json:"followed_by"`
}

// Relationship represents a pair of Twitter users' relationship with each other
type Relationship struct {
	Target RelationshipTarget `json:"target"`
	Source RelationshipTarget `json:"source"`
}

// Friendship represents a relationship between two Twitter users
type Friendship struct {
	Relationship Relationship `json:"relationship"`
}

// FriendshipLookup represents a Twitter user's relationship to currently authorized user
type FriendshipLookup struct {
	Name        string   `json:"name"`
	ScreenName  string   `json:"screen_name"`
	ID          int64    `json:"id"`
	IDStr       string   `json:"id_str"`
	Connections []string `json:"connections"`
}

// Image represents info on a Twitter image
type Image struct {
	ImageType string `json:"image_type"`
	W         int    `json:"w"`
	H         int    `json:"h"`
}

// Video represents info on a Twitter video
type Video struct {
	VideoType string `json:"video_type"`
}

// MediaUpload represents a Twitter's response object for media upload
type MediaUpload struct {
	MediaID          int64  `json:"media_id"`
	MediaIDString    string `json:"media_id_string"`
	Size             int    `json:"size"`
	ExpiresAfterSecs int    `json:"expires_after_secs"`
	Image            Image  `json:"image"`
	Video            Video  `json:"video"`
}
<commit_msg>Add the extended_tweet field<commit_after>package twitter

import "time"

// Tweet represents a Twitter tweet object.
type Tweet struct {
	Contributor          []Contributor          `json:"contributors"`
	Coordinates          *Coordinates           `json:"coordinates"`
	CreatedAt            string                 `json:"created_at"`
	Entities             Entities               `json:"entities"`
	ExtendedEntities     ExtendedEntities       `json:"extended_entities"`
	ExtendedTweet        *ExtendedTweet         `json:"extended_tweet"`
	FavoriteCount        int                    `json:"favorite_count"`
	Favorited            bool                   `json:"favorited"`
	FilterLevel          string                 `json:"filter_level"`
	ID                   int64                  `json:"id"`
	IDStr                string                 `json:"id_str"`
	InReplyToScreenName  string                 `json:"in_reply_to_screen_name"`
	InReplyToStatusID    int64                  `json:"in_reply_to_status_id"`
	InReplyToStatusIDStr string                 `json:"in_reply_to_status_id_str"`
	InReplyToUserID      int64                  `json:"in_reply_to_user_id"`
	InReplyToUserIDStr   string                 `json:"in_reply_to_user_id_str"`
	Lang                 string                 `json:"lang"`
	Place                *Place                 `json:"place"`
	PossiblySensitive    bool                   `json:"possibly_sensitive"`
	QuotedStatusID       int64                  `json:"quoted_status_id"`
	QuotedStatusIDStr    string                 `json:"quoted_status_id_str"`
	QuotedStatus         *Tweet                 `json:"quoted_status"`
	RetweetCount         int                    `json:"retweet_count"`
	Retweeted            bool                   `json:"retweeted"`
	RetweetedStatus      *Tweet                 `json:"retweeted_status"`
	Scopes               map[string]interface{} `json:"scopes"`
	Source               string                 `json:"source"`
	Text                 string                 `json:"text"`
	Truncated            bool                   `json:"truncated"`
	User                 User                   `json:"user"`
	WithheldCopyright    bool                   `json:"withheld_copyright"`
	WithheldInCountries  []string               `json:"withheld_in_countries"`
	WithheldScope        string                 `json:"withheld_scope"`
}

// ExtendedTweet represents the full information for an extended tweet.
type ExtendedTweet struct {
	FullText         string           `json:"full_text"`
	DisplayTextRange []int            `json:"display_text_range"`
	Entities         Entities         `json:"entities"`
	ExtendedEntities ExtendedEntities `json:"extended_entities"`
}

// Coordinates represents the geographic location of a Tweet as reported by the
// user or client application. The coordinates array is formatted as geoJSON
// (longitude first, then latitude).
type Coordinates struct {
	Coordinates [2]float64 `json:"coordinates"`
	Type        string     `json:"type"`
}

// DirectMessage ...
type DirectMessage struct {
	CreatedAt           string   `json:"created_at"`
	Entities            Entities `json:"entities"`
	ID                  int64    `json:"id"`
	IDStr               string   `json:"id_str"`
	Recipient           User     `json:"recipient"`
	RecipientID         int64    `json:"recipient_id"`
	RecipientScreenName string   `json:"recipient_screen_name"`
	Sender              User     `json:"sender"`
	SenderID            int64    `json:"sender_id"`
	SenderScreenName    string   `json:"sender_screen_name"`
	Text                string   `json:"text"`
}

// ExtendedEntities provides metadata about the media entities present.
type ExtendedEntities struct {
	Media []MediaEntity `json:"media"`
}

// Entities provides metadata and additonal contextual information about twitter
// content.
type Entities struct {
	Hashtags     []HashtagEntity     `json:"hashtags"`
	Media        []MediaEntity       `json:"media"`
	URL          URLEntities         `json:"url"`
	URLs         []URLEntity         `json:"urls"`
	UserMentions []UserMentionEntity `json:"user_mentions"`
}

// HashtagEntity represents hashtags which have been parsed out of the Tweet
// text.
type HashtagEntity struct {
	Indices []int  `json:"indices"`
	Text    string `json:"text"`
}

// MediaEntity represents media elements uploaded with a Tweet.
type MediaEntity struct {
	DisplayURL        string     `json:"display_url"`
	ExpandedURL       string     `json:"expanded_url"`
	ID                int64      `json:"id"`
	IDStr             string     `json:"id_str"`
	Indices           []int      `json:"indices"`
	MediaURL          string     `json:"media_url"`
	MediaURLHTTPS     string     `json:"media_url_https"`
	Sizes             MediaSizes `json:"sizes"`
	SourceStatusID    int64      `json:"source_status_id"`
	SourceStatusIDStr string     `json:"source_status_id_str"`
	Type              string     `json:"type"`
	URL               string     `json:"url"`
	VideoInfo         VideoInfo  `json:"video_info"`
}

// VideoInfo represents information specific to videos.
type VideoInfo struct {
	AspectRatio    []int          `json:"aspect_ratio"`
	DurationMillis int            `json:"duration_millis"`
	Variants       []VideoVariant `json:"variants"`
}

// VideoVariant represents video info for a single bitrate.
type VideoVariant struct {
	Bitrate     int    `json:"bitrate"`
	ContentType string `json:"content_type"`
	URL         string `json:"url"`
}

// MediaSize represents the height, width, and resize string of a media element.
type MediaSize struct {
	Height int    `json:"h"`
	Resize string `json:"resize"`
	Width  int    `json:"w"`
}

// MediaSizes represents a range of media sizes.
type MediaSizes struct {
	Large  MediaSize `json:"large"`
	Medium MediaSize `json:"medium"`
	Small  MediaSize `json:"small"`
	Thumb  MediaSize `json:"thumb"`
}

// UserMentionEntity represents other Twitter users mentioned in the text of a
// tweet.
type UserMentionEntity struct {
	ID         int64  `json:"id"`
	IDStr      string `json:"id_str"`
	Indices    []int  `json:"indices"`
	Name       string `json:"name"`
	ScreenName string `json:"screen_name"`
}

// BoundingBox represents a bounding box of coordinates which encloses a Place.
type BoundingBox struct {
	Coordinates [][][2]float64 `json:"coordinates"`
	Type        string         `json:"type"`
}

// Place represents a specific named location with corresponding geo
// coordinates.
type Place struct {
	Attributes  map[string]string `json:"attributes"`
	BoundingBox BoundingBox       `json:"bounding_box"`
	Country     string            `json:"country"`
	CountryCode string            `json:"country_code"`
	FullName    string            `json:"full_name"`
	ID          string            `json:"id"`
	Name        string            `json:"name"`
	PlaceType   string            `json:"place_type"`
	URL         string            `json:"url"`
}

// Contributor represents a user who contributed to the authorship of a tweet.
type Contributor struct {
	ID         int64  `json:"id"`
	IDStr      string `json:"id_str"`
	ScreenName string `json:"screen_name"`
}

// CreatedAtTime returns a time.Time version of the created date.
func (t *Tweet) CreatedAtTime() (time.Time, error) {
	return time.Parse(time.RubyDate, t.CreatedAt)
}

// URLEntities represents a list of url entities.
type URLEntities struct {
	URLs []URLEntity `json:"urls"`
}

// URLEntity represents URLs included in the text of a Tweet or within textual
// fields of a user object.
type URLEntity struct {
	DisplayURL  string `json:"display_url"`
	ExpandedURL string `json:"expanded_url"`
	Indices     []int  `json:"indices"`
	URL         string `json:"url"`
}

// User represents a twitter user object, as received using the twitter REST
// API.
type User struct {
	ContributorsEnabled            bool     `json:"contributors_enabled"`
	CreatedAt                      string   `json:"created_at"`
	DefaultProfile                 bool     `json:"default_profile"`
	DefaultProfileImage            bool     `json:"default_profile_image"`
	Description                    string   `json:"description"`
	Entities                       Entities `json:"entities"`
	FavouritesCount                int      `json:"favourites_count"`
	FollowRequestSent              bool     `json:"follow_request_sent"`
	Following                      bool     `json:"following"`
	FollowersCount                 int      `json:"followers_count"`
	FriendsCount                   int      `json:"friends_count"`
	GeoEnabled                     bool     `json:"geo_enabled"`
	ID                             int64    `json:"id"`
	IDStr                          string   `json:"id_str"`
	IsTranslator                   bool     `json:"is_translator"`
	Lang                           string   `json:"lang"`
	ListedCount                    int      `json:"listed_count"`
	Location                       string   `json:"location"`
	Name                           string   `json:"name"`
	Notifications                  bool     `json:"notifications"`
	ProfileBackgroundColor         string   `json:"profile_background_color"`
	ProfileBackgroundImageURL      string   `json:"profile_background_image_url"`
	ProfileBackgroundImageURLHTTPS string   `json:"profile_background_image_url_https"`
	ProfileBackgroundTile          bool     `json:"profile_background_tile"`
	ProfileBannerURL               string   `json:"profile_banner_url"`
	ProfileImageURL                string   `json:"profile_image_url"`
	ProfileImageURLHTTPS           string   `json:"profile_image_url_https"`
	ProfileLinkColor               string   `json:"profile_link_color"`
	ProfileSidebarBorderColor      string   `json:"profile_sidebar_border_color"`
	ProfileSidebarFillColor        string   `json:"profile_sidebar_fill_color"`
	ProfileTextColor               string   `json:"profile_text_color"`
	ProfileUseBackgroundImage      bool     `json:"profile_use_background_image"`
	Protected                      bool     `json:"protected"`
	ScreenName                     string   `json:"screen_name"`
	ShowAllInlineMedia             bool     `json:"show_all_inline_media"`
	Status                         *Tweet   `json:"status"` // can be null
	StatusesCount                  int      `json:"statuses_count"`
	TimeZone                       string   `json:"time_zone"`
	URL                            string   `json:"url"`
	UTCOffset                      int      `json:"utc_offset"`
	Verified                       bool     `json:"verified"`
	WithheldInCountries            string   `json:"withheld_in_countries"`
	WithheldScope                  string   `json:"withheld_scope"`
}

// Configuration represents the configuration object received from Twitter help/configuration endpoint
type Configuration struct {
	CharactersReservedPerMedia int                  `json:"characters_reserved_per_media"`
	DMTextCharacterLimit       int                  `json:"dm_text_character_limit"`
	MaxMediaPerUpload          int                  `json:"max_media_per_upload"`
	PhotoSizeLimit             int                  `json:"photo_size_limit"`
	PhotoSizes                 map[string]PhotoSize `json:"photo_sizes"`
	ShortURLLength             int                  `json:"short_url_length"`
	ShortURLLengthHTTPS        int                  `json:"short_url_length_https"`
	NonUsernamePaths           []string             `json:"non_username_paths"`
}

// PhotoSize represents the photo size object iside configuration objectt
type PhotoSize struct {
	H      int    `json:"h"`
	Resize string `json:"resize"`
	W      int    `json:"w"`
}

// Language represents the language obejct received from Twitter help/langauges
type Language struct {
	Code   string `json:"code"`
	Status string `json:"status"`
	Name   string `json:"name"`
}

// OEmbed represents the response body for an oembed request.
type OEmbed struct {
	CacheAge     string `json:"cache_age"`
	URL          string `json:"url"`
	ProviderURL  string `json:"provider_url"`
	ProviderName string `json:"provider_name"`
	AuthorName   string `json:"author_name"`
	Version      string `json:"version"`
	AuthorURL    string `json:"author_url"`
	Type         string `json:"type"`
	HTML         string `json:"html"`
	Height       *int   `json:"height"`
	Width        *int   `json:"width"`
}

// IDs represents a paginated list of string IDs.
type IDs struct {
	IDs               []string `json:"ids"`
	PreviousCursor    int64    `json:"previous_cursor"`
	PreviousCursotStr string   `json:"previous_cursor_str"`
	NextCursor        int64    `json:"next_cursor"`
	NextCursorStr     string   `json:"next_cursor_str"`
}

// RelationshipTarget represents a Twitter user's relationship with a source
type RelationshipTarget struct {
	IDStr      string `json:"id_str"`
	ID         int64  `json:"id"`
	ScreenName string `json:"screen_name"`
	Following  bool   `json:"following"`
	FollowedBy bool   `json:"followed_by"`
}

// Relationship represents a pair of Twitter users' relationship with each other
type Relationship struct {
	Target RelationshipTarget `json:"target"`
	Source RelationshipTarget `json:"source"`
}

// Friendship represents a relationship between two Twitter users
type Friendship struct {
	Relationship Relationship `json:"relationship"`
}

// FriendshipLookup represents a Twitter user's relationship to currently authorized user
type FriendshipLookup struct {
	Name        string   `json:"name"`
	ScreenName  string   `json:"screen_name"`
	ID          int64    `json:"id"`
	IDStr       string   `json:"id_str"`
	Connections []string `json:"connections"`
}

// Image represents info on a Twitter image
type Image struct {
	ImageType string `json:"image_type"`
	W         int    `json:"w"`
	H         int    `json:"h"`
}

// Video represents info on a Twitter video
type Video struct {
	VideoType string `json:"video_type"`
}

// MediaUpload represents a Twitter's response object for media upload
type MediaUpload struct {
	MediaID          int64  `json:"media_id"`
	MediaIDString    string `json:"media_id_string"`
	Size             int    `json:"size"`
	ExpiresAfterSecs int    `json:"expires_after_secs"`
	Image            Image  `json:"image"`
	Video            Video  `json:"video"`
}
<|endoftext|>