<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"sort"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/go-martini/martini"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/martini-contrib/binding"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/martini-contrib/render"
	"github.com/flynn/flynn/router/types"
)

func apiHandler(rtr *Router) http.Handler {
	r := martini.NewRouter()
	m := martini.New()
	m.Map(log.New(os.Stdout, "[router] ", log.LstdFlags|log.Lmicroseconds))
	m.Use(martini.Logger())
	m.Use(martini.Recovery())
	m.Use(render.Renderer())
	m.Action(r.Handle)
	m.Map(rtr)

	r.Post("/routes", binding.Bind(router.Route{}), createRoute)
	r.Put("/routes", binding.Bind(router.Route{}), createOrReplaceRoute)
	r.Get("/routes", getRoutes)
	r.Get("/routes/:route_type/:route_id", getRoute)
	r.Delete("/routes/:route_type/:route_id", deleteRoute)
	return m
}

func createRoute(req *http.Request, route router.Route, router *Router, r render.Render) {
	now := time.Now()
	route.CreatedAt = &now
	route.UpdatedAt = &now

	l := listenerFor(router, route.Type)
	if l == nil {
		r.JSON(400, "Invalid route type")
		return
	}

	if err := l.AddRoute(&route); err != nil {
		log.Println(err)
		r.JSON(500, struct{}{})
		return
	}
	res := formatRoute(&route)
	r.JSON(200, res)
}

func createOrReplaceRoute(req *http.Request, route router.Route, router *Router, r render.Render) {
	now := time.Now()
	route.CreatedAt = &now
	route.UpdatedAt = &now

	l := listenerFor(router, route.Type)
	if l == nil {
		r.JSON(400, "Invalid route type")
		return
	}

	if err := l.SetRoute(&route); err != nil {
		log.Println(err)
		r.JSON(500, struct{}{})
		return
	}
	res := formatRoute(&route)
	r.JSON(200, res)
}

func listenerFor(router *Router, typ string) Listener {
	switch typ {
	case "http":
		return router.HTTP
	case "tcp":
		return router.TCP
	default:
		return nil
	}
}

func formatRoute(r *router.Route) *router.Route {
	r.ID = fmt.Sprintf("%s/%s", r.Type, r.ID)
	switch r.Type {
	case "http":
		httpRoute := r.HTTPRoute()
		httpRoute.TLSKey = ""
		httpRoute.Route = nil
		conf, _ := json.Marshal(httpRoute)
		jsonConf := json.RawMessage(conf)
		r.Config = &jsonConf
	}
	return r
}

type sortedRoutes []*router.Route

func (p sortedRoutes) Len() int           { return len(p) }
func (p sortedRoutes) Less(i, j int) bool { return p[i].CreatedAt.After(*p[j].CreatedAt) }
func (p sortedRoutes) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func getRoutes(req *http.Request, rtr *Router, r render.Render) {
	routes, err := rtr.HTTP.List()
	if err != nil {
		log.Println(err)
		r.JSON(500, struct{}{})
		return
	}
	tcpRoutes, err := rtr.TCP.List()
	if err != nil {
		log.Println(err)
		r.JSON(500, struct{}{})
		return
	}
	routes = append(routes, tcpRoutes...)

	if ref := req.URL.Query().Get("parent_ref"); ref != "" {
		filtered := make([]*router.Route, 0)
		for _, route := range routes {
			if route.ParentRef == ref {
				filtered = append(filtered, route)
			}
		}
		routes = filtered
	}
	for i, route := range routes {
		routes[i] = formatRoute(route)
	}

	sort.Sort(sortedRoutes(routes))
	r.JSON(200, routes)
}

func getRoute(params martini.Params, router *Router, r render.Render) {
	l := listenerFor(router, params["route_type"])
	if l == nil {
		r.JSON(404, struct{}{})
		return
	}

	route, err := l.Get(params["route_id"])
	if err == ErrNotFound {
		r.JSON(404, struct{}{})
		return
	}
	if err != nil {
		log.Println(err)
		r.JSON(500, struct{}{})
		return
	}

	r.JSON(200, formatRoute(route))
}

func deleteRoute(params martini.Params, router *Router, r render.Render) {
	l := listenerFor(router, params["route_type"])
	if l == nil {
		r.JSON(404, struct{}{})
		return
	}

	err := l.RemoveRoute(params["route_id"])
	if err == ErrNotFound {
		r.JSON(404, struct{}{})
		return
	}
	if err != nil {
		log.Println(err)
		r.JSON(500, struct{}{})
		return
	}

	r.JSON(200, struct{}{})
}<commit_msg>router: Use strings for all HTTP API errors<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"sort"
	"time"

	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/go-martini/martini"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/martini-contrib/binding"
	"github.com/flynn/flynn/Godeps/_workspace/src/github.com/martini-contrib/render"
	"github.com/flynn/flynn/router/types"
)

func apiHandler(rtr *Router) http.Handler {
	r := martini.NewRouter()
	m := martini.New()
	m.Map(log.New(os.Stdout, "[router] ", log.LstdFlags|log.Lmicroseconds))
	m.Use(martini.Logger())
	m.Use(martini.Recovery())
	m.Use(render.Renderer())
	m.Action(r.Handle)
	m.Map(rtr)

	r.Post("/routes", binding.Bind(router.Route{}), createRoute)
	r.Put("/routes", binding.Bind(router.Route{}), createOrReplaceRoute)
	r.Get("/routes", getRoutes)
	r.Get("/routes/:route_type/:route_id", getRoute)
	r.Delete("/routes/:route_type/:route_id", deleteRoute)
	return m
}

func createRoute(req *http.Request, route router.Route, router *Router, r render.Render) {
	now := time.Now()
	route.CreatedAt = &now
	route.UpdatedAt = &now

	l := listenerFor(router, route.Type)
	if l == nil {
		r.JSON(400, "Invalid route type")
		return
	}

	if err := l.AddRoute(&route); err != nil {
		log.Println(err)
		r.JSON(500, "unknown error")
		return
	}
	res := formatRoute(&route)
	r.JSON(200, res)
}

func createOrReplaceRoute(req *http.Request, route router.Route, router *Router, r render.Render) {
	now := time.Now()
	route.CreatedAt = &now
	route.UpdatedAt = &now

	l := listenerFor(router, route.Type)
	if l == nil {
		r.JSON(400, "Invalid route type")
		return
	}

	if err := l.SetRoute(&route); err != nil {
		log.Println(err)
		r.JSON(500, "unknown error")
		return
	}
	res := formatRoute(&route)
	r.JSON(200, res)
}

func listenerFor(router *Router, typ string) Listener {
	switch typ {
	case "http":
		return router.HTTP
	case "tcp":
		return router.TCP
	default:
		return nil
	}
}

func formatRoute(r *router.Route) *router.Route {
	r.ID = fmt.Sprintf("%s/%s", r.Type, r.ID)
	switch r.Type {
	case "http":
		httpRoute := r.HTTPRoute()
		httpRoute.TLSKey = ""
		httpRoute.Route = nil
		conf, _ := json.Marshal(httpRoute)
		jsonConf := json.RawMessage(conf)
		r.Config = &jsonConf
	}
	return r
}

type sortedRoutes []*router.Route

func (p sortedRoutes) Len() int           { return len(p) }
func (p sortedRoutes) Less(i, j int) bool { return p[i].CreatedAt.After(*p[j].CreatedAt) }
func (p sortedRoutes) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func getRoutes(req *http.Request, rtr *Router, r render.Render) {
	routes, err := rtr.HTTP.List()
	if err != nil {
		log.Println(err)
		r.JSON(500, "unknown error")
		return
	}
	tcpRoutes, err := rtr.TCP.List()
	if err != nil {
		log.Println(err)
		r.JSON(500, "unknown error")
		return
	}
	routes = append(routes, tcpRoutes...)

	if ref := req.URL.Query().Get("parent_ref"); ref != "" {
		filtered := make([]*router.Route, 0)
		for _, route := range routes {
			if route.ParentRef == ref {
				filtered = append(filtered, route)
			}
		}
		routes = filtered
	}
	for i, route := range routes {
		routes[i] = formatRoute(route)
	}

	sort.Sort(sortedRoutes(routes))
	r.JSON(200, routes)
}

func getRoute(params martini.Params, router *Router, r render.Render) {
	l := listenerFor(router, params["route_type"])
	if l == nil {
		r.JSON(404, "not found")
		return
	}

	route, err := l.Get(params["route_id"])
	if err == ErrNotFound {
		r.JSON(404, "not found")
		return
	}
	if err != nil {
		log.Println(err)
		r.JSON(500, "unknown error")
		return
	}

	r.JSON(200, formatRoute(route))
}

func deleteRoute(params martini.Params, router *Router, r render.Render) {
	l := listenerFor(router, params["route_type"])
	if l == nil {
		r.JSON(404, "not found")
		return
	}

	err := l.RemoveRoute(params["route_id"])
	if err == ErrNotFound {
		r.JSON(404, "not found")
		return
	}
	if err != nil {
		log.Println(err)
		r.JSON(500, "unknown error")
		return
	}

	r.JSON(200, "unknown error")
}
{'fraction_non_alphanumeric': 0.1737991266375546, 'fraction_numerical': 0.012975670617592016, 'mean_word_length': 3.878880097382836, 'pattern_counts': {'":': 6, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6221777', 'n_tokens_mistral': 3298, 'n_tokens_neox': 3116, 'n_words': 660}