// setupHandlerChain does plumbing for logging and such. func setupHandlerChain(c *xhandler.Chain) { host, _ := os.Hostname() conf := xlog.Config{ // Log info level and higher Level: xlog.LevelInfo, // Set some global env fields Fields: xlog.F{ "role": "my-shitty-service", "host": host, }, // Output everything on console Output: xlog.NewOutputChannel(xlog.NewConsoleOutput()), } // Add close notifier handler so context is cancelled when the client closes // the connection c.UseC(xhandler.CloseHandler) // Install the logger handler c.UseC(xlog.NewHandler(conf)) // Add timeout handler (HAHA) //c.UseC(xhandler.TimeoutHandler(2 * time.Second)) // Install some provided extra handler to set some request's context fields. // Thanks to those handler, all our logs will come with some pre-populated fields. c.UseC(xlog.MethodHandler("method")) c.UseC(xlog.URLHandler("url")) c.UseC(xlog.RemoteAddrHandler("ip")) c.UseC(xlog.UserAgentHandler("user_agent")) c.UseC(xlog.RefererHandler("referer")) c.UseC(xlog.RequestIDHandler("req_id", "Request-Id")) }
func Example_handler() { c := alice.New() host, _ := os.Hostname() conf := xlog.Config{ // Set some global env fields Fields: xlog.F{ "role": "my-service", "host": host, }, } // Install the logger handler with default output on the console c.Append(xlog.NewHandler(conf)) // Plug the xlog handler's input to Go's default logger log.SetFlags(0) log.SetOutput(xlog.New(conf)) // Install some provided extra handler to set some request's context fields. // Thanks to those handler, all our logs will come with some pre-populated fields. c.Append(xlog.RemoteAddrHandler("ip")) c.Append(xlog.UserAgentHandler("user_agent")) c.Append(xlog.RefererHandler("referer")) c.Append(xlog.RequestIDHandler("req_id", "Request-Id")) // Here is your final handler h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Get the logger from the request's context. You can safely assume it // will be always there: if the handler is removed, xlog.FromContext // will return a NopLogger l := xlog.FromRequest(r) // Then log some errors if err := errors.New("some error from elsewhere"); err != nil { l.Errorf("Here is an error: %v", err) } // Or some info with fields l.Info("Something happend", xlog.F{ "user": "******", "status": "ok", }) })) http.Handle("/", h) if err := http.ListenAndServe(":8080", nil); err != nil { log.SetOutput(os.Stderr) // make sure we print to console log.Fatal(err) } }
func main() { index := resource.NewIndex() users := index.Bind("users", user, mem.NewHandler(), resource.Conf{ AllowedModes: resource.ReadWrite, }) users.Alias("admin", url.Values{"filter": []string{`{"admin": true}`}}) posts := index.Bind("posts", post, mem.NewHandler(), resource.Conf{ AllowedModes: resource.ReadWrite, }) posts.Bind("followers", "post", postFollower, mem.NewHandler(), resource.Conf{ AllowedModes: resource.ReadWrite, }) // Create API HTTP handler for the resource graph api, err := rest.NewHandler(index) if err != nil { log.Fatalf("Invalid API configuration: %s", err) } // Setup logger c := alice.New() c = c.Append(xlog.NewHandler(xlog.Config{})) c = c.Append(xaccess.NewHandler()) c = c.Append(xlog.RequestHandler("req")) c = c.Append(xlog.RemoteAddrHandler("ip")) c = c.Append(xlog.UserAgentHandler("ua")) c = c.Append(xlog.RefererHandler("ref")) c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id")) resource.LoggerLevel = resource.LogLevelDebug resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) { xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields) } // Bind the API under /api/ path http.Handle("/api/", http.StripPrefix("/api/", c.Then(api))) // Create and bind the graphql endpoint graphql, err := graphql.NewHandler(index) if err != nil { log.Fatal(err) } http.Handle("/graphql", c.Then(graphql)) http.HandleFunc("/graphiql", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` <!DOCTYPE html> <html> <head> <style> html, body {height: 100%; margin: 0; overflow: hidden; width: 100%;} </style> <link href="//cdn.jsdelivr.net/graphiql/0.4.9/graphiql.css" rel="stylesheet" /> <script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script> <script src="//cdn.jsdelivr.net/react/0.14.7/react.min.js"></script> <script src="//cdn.jsdelivr.net/react/0.14.7/react-dom.min.js"></script> <script src="//cdn.jsdelivr.net/graphiql/0.4.9/graphiql.min.js"></script> </head> <body> <script> // Collect the URL parameters var parameters = {}; window.location.search.substr(1).split('&').forEach(function (entry) { var eq = entry.indexOf('='); if (eq >= 0) { parameters[decodeURIComponent(entry.slice(0, eq))] = decodeURIComponent(entry.slice(eq + 1)); } }); // Produce a Location query string from a parameter object. function locationQuery(params) { return '/graphql?' + Object.keys(params).map(function (key) { return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); }).join('&'); } // Derive a fetch URL from the current URL, sans the GraphQL parameters. var graphqlParamNames = { query: true, variables: true, operationName: true }; var otherParams = {}; for (var k in parameters) { if (parameters.hasOwnProperty(k) && graphqlParamNames[k] !== true) { otherParams[k] = parameters[k]; } } var fetchURL = locationQuery(otherParams); // Defines a GraphQL fetcher using the fetch API. function graphQLFetcher(graphQLParams) { return fetch(fetchURL, { method: 'post', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(graphQLParams), credentials: 'include', }).then(function (response) { return response.text(); }).then(function (responseBody) { try { return JSON.parse(responseBody); } catch (error) { return responseBody; } }); } // When the query and variables string is edited, update the URL bar so // that it can be easily shared. function onEditQuery(newQuery) { parameters.query = newQuery; updateURL(); } function onEditVariables(newVariables) { parameters.variables = newVariables; updateURL(); } function updateURL() { history.replaceState(null, null, locationQuery(parameters)); } // Render <GraphiQL /> into the body. React.render( React.createElement(GraphiQL, { fetcher: graphQLFetcher, onEditQuery: onEditQuery, onEditVariables: onEditVariables, defaultQuery: "{\ postsList{\ i: id,\ m: meta{\ t: title,\ b: body},\ thumb_small_url: thumbnail_url(height:80)\ }\ }", }), document.body ); </script> </body> </html>`)) }) // Inject some fixtures fixtures := [][]string{ {"PUT", "/users/johndoe", `{"name": "John Doe", "ip": "1.2.3.4", "password": "******", "admin": true}`}, {"PUT", "/users/fan1", `{"name": "Fan 1", "ip": "1.2.3.4", "password": "******"}}`}, {"PUT", "/users/fan2", `{"name": "Fan 2", "ip": "1.2.3.4", "password": "******"}}`}, {"PUT", "/users/fan3", `{"name": "Fan 3", "ip": "1.2.3.4", "password": "******"}}`}, {"PUT", "/users/fan4", `{"name": "Fan 4", "ip": "1.2.3.4", "password": "******"}}`}, {"PUT", "/posts/ar5qrgukj5l7a6eq2ps0", `{ "user": "******", "thumbnail_url": "http://dom.com/image.png", "meta": { "title": "First Post", "body": "This is my first post" } }`}, {"POST", "/posts/ar5qrgukj5l7a6eq2ps0/followers", `{"user": "******"}`}, {"POST", "/posts/ar5qrgukj5l7a6eq2ps0/followers", `{"user": "******"}`}, {"POST", "/posts/ar5qrgukj5l7a6eq2ps0/followers", `{"user": "******"}`}, } for _, fixture := range fixtures { req, err := http.NewRequest(fixture[0], fixture[1], strings.NewReader(fixture[2])) if err != nil { log.Fatal(err) } w := httptest.NewRecorder() api.ServeHTTP(w, req) if w.Code >= 400 { log.Fatalf("Error returned for `%s %s`: %v", fixture[0], fixture[1], w) } } // Serve it log.Print("Serving API on http://localhost:8080") log.Print("Visit http://localhost:8080/graphiql for a GraphiQL UI") log.Println("Play with (httpie):\n", "- http :8080/graphql query=='{postsList{id,thumb_s_url:thumbnail_url(height:80)}}'\n", "- http :8080/graphql query=='{postsList{i:id,m:meta{t:title, b:body},thumb_small_url:thumbnail_url(height:80)}}'\n", "- http :8080/graphql query=='{postsList{id,meta{title},user{id,name}}}'\n", "- http :8080/graphql query=='{posts(id:\"ar5qrgukj5l7a6eq2ps0\"){followers{post{id,meta{title}},user{id,name}}}}'\n", "- http :8080/graphql query=='{posts(id:\"ar5qrgukj5l7a6eq2ps0\"){id,meta{title},followers(limit:2){user{id,name}}}}'") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } }
func main() { flag.Parse() // Create a REST API resource index index := resource.NewIndex() // Bind user on /users users := index.Bind("users", user, mem.NewHandler(), resource.Conf{ AllowedModes: resource.ReadWrite, }) // Init the db with some users (user registration is not handled by this example) secret, _ := schema.Password{}.Validate("secret") users.Insert(context.Background(), []*resource.Item{ {ID: "admin", Updated: time.Now(), ETag: "abcd", Payload: map[string]interface{}{ "id": "jack", "name": "Jack Sparrow", "password": secret, }}, {ID: "john", Updated: time.Now(), ETag: "efgh", Payload: map[string]interface{}{ "id": "john", "name": "John Doe", "password": secret, }}, }) // Bind post on /posts posts := index.Bind("posts", post, mem.NewHandler(), resource.Conf{ AllowedModes: resource.ReadWrite, }) // Protect resources users.Use(AuthResourceHook{UserField: "id"}) posts.Use(AuthResourceHook{UserField: "user"}) // Create API HTTP handler for the resource graph api, err := rest.NewHandler(index) if err != nil { log.Fatalf("Invalid API configuration: %s", err) } // Setup logger c := alice.New() c = c.Append(xlog.NewHandler(xlog.Config{})) c = c.Append(xaccess.NewHandler()) c = c.Append(xlog.RequestHandler("req")) c = c.Append(xlog.RemoteAddrHandler("ip")) c = c.Append(xlog.UserAgentHandler("ua")) c = c.Append(xlog.RefererHandler("ref")) c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id")) resource.LoggerLevel = resource.LogLevelDebug resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) { xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields) } // Setup auth middleware jwtSecretBytes := []byte(*jwtSecret) c = c.Append(NewJWTHandler(users, func(t *jwt.Token) (interface{}, error) { println("paf") if t.Method != jwt.SigningMethodHS256 { return nil, jwt.ErrInvalidKey } return jwtSecretBytes, nil })) // Bind the API under / http.Handle("/", c.Then(api)) // Demo tokens jackToken := jwt.New(jwt.SigningMethodHS256) jackClaims := jackToken.Claims.(jwt.MapClaims) jackClaims["user_id"] = "jack" jackTokenString, err := jackToken.SignedString(jwtSecretBytes) if err != nil { log.Fatal(err) } johnToken := jwt.New(jwt.SigningMethodHS256) johnClaims := johnToken.Claims.(jwt.MapClaims) johnClaims["user_id"] = "john" johnTokenString, err := johnToken.SignedString(jwtSecretBytes) if err != nil { log.Fatal(err) } // Serve it log.Print("Serving API on http://localhost:8080") log.Printf("Your token secret is %q, change it with the `-jwt-secret' flag", *jwtSecret) log.Print("Play with tokens:\n", "\n", "- http :8080/posts access_token==", johnTokenString, " title=\"John's post\"\n", "- http :8080/posts access_token==", johnTokenString, "\n", "- http :8080/posts\n", "\n", "- http :8080/posts access_token==", jackTokenString, " title=\"Jack's post\"\n", "- http :8080/posts access_token==", jackTokenString, "\n", ) if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } }
func main() { // Create a REST API resource index index := resource.NewIndex() // Bind user on /users users := index.Bind("users", user, mem.NewHandler(), resource.Conf{ AllowedModes: resource.ReadWrite, }) // Init the db with some users (user registration is not handled by this example) secret, _ := schema.Password{}.Validate("secret") users.Insert(context.Background(), []*resource.Item{ {ID: "admin", Updated: time.Now(), ETag: "abcd", Payload: map[string]interface{}{ "id": "admin", "name": "Dilbert", "password": secret, }}, {ID: "john", Updated: time.Now(), ETag: "efgh", Payload: map[string]interface{}{ "id": "john", "name": "John Doe", "password": secret, }}, }) // Bind post on /posts posts := index.Bind("posts", post, mem.NewHandler(), resource.Conf{ AllowedModes: resource.ReadWrite, }) // Protect resources users.Use(AuthResourceHook{UserField: "id"}) posts.Use(AuthResourceHook{UserField: "user"}) // Create API HTTP handler for the resource graph api, err := rest.NewHandler(index) if err != nil { log.Fatalf("Invalid API configuration: %s", err) } // Setup logger c := alice.New() c = c.Append(xlog.NewHandler(xlog.Config{})) c = c.Append(xaccess.NewHandler()) c = c.Append(xlog.RequestHandler("req")) c = c.Append(xlog.RemoteAddrHandler("ip")) c = c.Append(xlog.UserAgentHandler("ua")) c = c.Append(xlog.RefererHandler("ref")) c = c.Append(xlog.RequestIDHandler("req_id", "Request-Id")) resource.LoggerLevel = resource.LogLevelDebug resource.Logger = func(ctx context.Context, level resource.LogLevel, msg string, fields map[string]interface{}) { xlog.FromContext(ctx).OutputF(xlog.Level(level), 2, msg, fields) } // Setup auth middleware c = c.Append(NewBasicAuthHandler(users)) // Bind the API under / http.Handle("/", c.Then(api)) // Serve it log.Print("Serving API on http://localhost:8080") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatal(err) } }