<commit_before>package main

import  (
        "fmt"
        "os"
        "../go-todotxt"
        "github.com/spf13/cobra"
        "os/user"
        "strings"
        "strconv"
        "github.com/rakyll/globalconf"
        "flag"
        "regexp"
        "os/exec"
        "io/ioutil"
)

func extendedLoader(filename string) (todotxt.TaskList, error) {
        usr, err := user.Current()
        if err != nil {
                return nil, err
        }

        filename = strings.Replace(filename, "~", usr.HomeDir, -1)
        tasks := todotxt.LoadTaskList(filename)

        return tasks, nil
}

type flagValue struct {
        str string
}

func (f *flagValue) String() string {
        return f.str
}

func (f *flagValue) Set(value string) error {
        f.str = value
        return nil
}

func newFlagValue(val string) *flagValue {
        return &flagValue{str: val}
}

func main() {

        conf, _ := globalconf.New("gotodo")

        var numtasks bool
        var sortby string
        var finished bool
        var prettyformat string
        var filename string

        var flagFilename = flag.String("file", "", "Location of the todo.txt file.")

        var flags = make(map[string]string)

        var cmdConfig = &cobra.Command{
            Use:   "config [key] [value]",
            Short: "Show and sets config values",
            Long:  `Config can be used to see and also set configuration variables.`,
            Run: func(cmd *cobra.Command, args []string) {
                    if len(args) == 0{
                            fmt.Printf("Available config variables:\n\n")
                            for k := range flags {
                                    fmt.Printf("%s\n", k)
                            }
                    }
                    if len(args) == 1 {
                            val, exists := flags[args[0]]
                            if exists {
                                    fmt.Printf("%s\n", val)
                            } else {
                                    // otherwise exit with non-zero status
                                    os.Exit(1)
                            }
                    }
                    if len(args) == 2 {
                            _, exists := flags[args[0]]
                            if exists {
                                    f := &flag.Flag{Name: args[0], Value: newFlagValue(args[1])}
                                    conf.Set("", f)
                            } else {
                                    os.Exit(1)
                            }
                    }
            },
        }

        var cmdList = &cobra.Command{
            Use:   "list [keyword]",
            Short: "Lists tasks that contain keyword, if any",
            Long:  `List is the most basic command that is used for listing tasks.
                    You can specify a keyword as well as other options.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if numtasks {
                    fmt.Println(tasks.Len())
                } else {
                    tasks.Sort(sortby)

                    var filteredTasks todotxt.TaskList
                    for _, task := range tasks {
                        if (!task.Finished() && !finished) ||
                           (task.Finished() && finished) {
                           if (len(args) > 0) {
                                   match, _ := regexp.MatchString(args[0], task.Text())
                                   if match {
                                           filteredTasks = append(filteredTasks, task)
                                   }
                           } else {
                                   filteredTasks = append(filteredTasks, task)
                           }
                        }
                    }

                    for _, task := range filteredTasks {
                            task.SetIdPaddingBy(tasks)
                            fmt.Println(task.PrettyPrint(prettyformat))
                    }
                }
            },
        }
        cmdList.Flags().BoolVarP(&numtasks, "num-tasks", "n", false,
                                 "Show the number of tasks")
        cmdList.Flags().BoolVarP(&finished, "finished", "f", false,
                                 "Show finished tasks")
        cmdList.Flags().StringVarP(&sortby, "sort", "s", "prio",
                                   "Sort tasks by parameter (prio|date|len|prio-rev|date-rev|len-rev|id|rand)")
        cmdList.Flags().StringVarP(&prettyformat, "pretty", "", "%i %c %p %t%r",
                                   "Pretty print tasks")

        var cmdAdd = &cobra.Command{
            Use:   "add [task]",
            Short: "Adds a task to the todo list.",
            Long:  `Adds a task to the todo list.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                task := strings.Join(args, " ")
                tasks.Add(task)

                tasks.Save(filename)
            },
        }

        var nofinishdate bool
        var cmdDone = &cobra.Command{
            Use:   "done [taskid]",
            Short: "Marks task as done.",
            Long:  `Marks task as done.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what needs to be done?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                err = tasks.Done(taskid, !nofinishdate)
                if err != nil {
                        fmt.Printf("There was an error %v\n", err)
                }

                tasks.Save(filename)
            },
        }
        cmdDone.Flags().BoolVarP(&nofinishdate, "no-finish-date", "D", false,
                                        "Do not mark finished tasks with date.")

        var cmdArchive = &cobra.Command{
            Use:   "archive [taskid]",
            Short: "Archives task.",
            Long:  `Archives task.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what needs to be done?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                fmt.Printf("Archiving task %v\n", taskid)

                tasks.Save(filename)
            },
        }

        var setprio string
        var settodo string
        var cmdSet = &cobra.Command{
            Use:   "set [taskid]",
            Short: "Sets given task's parameters.",
            Long:  `Sets given task's parameters.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what do you want to edit?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                if len(setprio) > 0 {
                        tasks[taskid].SetPriority(setprio[0])
                        tasks[taskid].RebuildRawTodo()
                }

                if len(settodo) > 0 {
                        tasks[taskid].SetTodo(settodo)
                        tasks[taskid].RebuildRawTodo()
                }

                tasks.Save(filename)
            },
        }

        var cmdEdit = &cobra.Command{
            Use:   "edit [taskid]",
            Short: "Edit given task",
            Long:  `Edit given task with your prefered editor.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what do you want to edit?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                text := tasks[taskid].RawText()

                file, err := ioutil.TempFile(os.TempDir(), "gotodo")
                defer os.Remove(file.Name())

                if err != nil {
                        panic(err)
                }

                e := ioutil.WriteFile(file.Name(), []byte(text), 0644)
                if e != nil {
                        panic(e)
                }

                editor := os.Getenv("EDITOR")
                if len(editor) == 0 {
                        editor = "nano" //FIXME: saner default?
                }

                c := exec.Command(editor, file.Name())

                // nasty hack, see http://stackoverflow.com/a/12089980
                c.Stdin = os.Stdin
                c.Stdout = os.Stdout
                c.Stderr = os.Stderr

                er := c.Run()

                if er != nil {
                        fmt.Println(er.Error())
                        panic(er)
                }

                dat, err := ioutil.ReadFile(file.Name())
                if err != nil {
                        panic(err)
                }

                lines := strings.Split(string(dat), "\n")
                if len (lines) > 0 {
                        if tasks[taskid].RawText() != lines[0] {
                                tasks[taskid] = todotxt.ParseTask(lines[0], taskid)
                                fmt.Printf("Task %d updated to:\n%s\n", taskid, lines[0])
                        }
                }

                tasks.Save(filename)
            },
        }

        cmdSet.PersistentFlags().StringVarP(&setprio, "priority", "p", "",
                                     "Sets task's priority.")
        cmdSet.PersistentFlags().StringVarP(&settodo, "todo", "t", "",
                                     "Sets task's todo.")

        var GotodoCmd = &cobra.Command{
            Use:   "gotodo",
            Short: "Gotodo is a go implementation of todo.txt.",
            Long: `A small, fast and fun implementation of todo.txt`,
            Run: func(cmd *cobra.Command, args []string) {
                cmdList.Run(cmd, nil)
            },
        }

        GotodoCmd.PersistentFlags().StringVarP(&filename, "filename", "", "",
                                     "Load tasks from this file.")

        conf.ParseAll()

        // values here
        flags["file"] = *flagFilename

        // sadly, this is the best we can do right now
        if filename == "" {
                if *flagFilename == "" {
                        filename = "todo.txt"
                } else {
                        filename = *flagFilename
                }
        }

        GotodoCmd.AddCommand(cmdList)
        GotodoCmd.AddCommand(cmdAdd)
        GotodoCmd.AddCommand(cmdDone)
        GotodoCmd.AddCommand(cmdArchive)
        GotodoCmd.AddCommand(cmdSet)
        GotodoCmd.AddCommand(cmdEdit)
        GotodoCmd.AddCommand(cmdConfig)
        GotodoCmd.Execute()
}
<commit_msg>Added no-color listing option<commit_after>package main

import  (
        "fmt"
        "os"
        "../go-todotxt"
        "github.com/spf13/cobra"
        "os/user"
        "strings"
        "strconv"
        "github.com/rakyll/globalconf"
        "flag"
        "regexp"
        "os/exec"
        "io/ioutil"
)

func extendedLoader(filename string) (todotxt.TaskList, error) {
        usr, err := user.Current()
        if err != nil {
                return nil, err
        }

        filename = strings.Replace(filename, "~", usr.HomeDir, -1)
        tasks := todotxt.LoadTaskList(filename)

        return tasks, nil
}

type flagValue struct {
        str string
}

func (f *flagValue) String() string {
        return f.str
}

func (f *flagValue) Set(value string) error {
        f.str = value
        return nil
}

func newFlagValue(val string) *flagValue {
        return &flagValue{str: val}
}

func main() {

        conf, _ := globalconf.New("gotodo")

        var numtasks bool
        var sortby string
        var finished bool
        var prettyformat string
        var filename string
        var no_color bool

        var flagFilename = flag.String("file", "", "Location of the todo.txt file.")

        var flags = make(map[string]string)

        var cmdConfig = &cobra.Command{
            Use:   "config [key] [value]",
            Short: "Show and sets config values",
            Long:  `Config can be used to see and also set configuration variables.`,
            Run: func(cmd *cobra.Command, args []string) {
                    if len(args) == 0{
                            fmt.Printf("Available config variables:\n\n")
                            for k := range flags {
                                    fmt.Printf("%s\n", k)
                            }
                    }
                    if len(args) == 1 {
                            val, exists := flags[args[0]]
                            if exists {
                                    fmt.Printf("%s\n", val)
                            } else {
                                    // otherwise exit with non-zero status
                                    os.Exit(1)
                            }
                    }
                    if len(args) == 2 {
                            _, exists := flags[args[0]]
                            if exists {
                                    f := &flag.Flag{Name: args[0], Value: newFlagValue(args[1])}
                                    conf.Set("", f)
                            } else {
                                    os.Exit(1)
                            }
                    }
            },
        }

        var cmdList = &cobra.Command{
            Use:   "list [keyword]",
            Short: "Lists tasks that contain keyword, if any",
            Long:  `List is the most basic command that is used for listing tasks.
                    You can specify a keyword as well as other options.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if numtasks {
                    fmt.Println(tasks.Len())
                } else {
                    tasks.Sort(sortby)

                    if no_color {
                        prettyformat = "%i %p %t%r"
                    }

                    var filteredTasks todotxt.TaskList
                    for _, task := range tasks {
                        if (!task.Finished() && !finished) ||
                           (task.Finished() && finished) {
                           if (len(args) > 0) {
                                   match, _ := regexp.MatchString(args[0], task.Text())
                                   if match {
                                           filteredTasks = append(filteredTasks, task)
                                   }
                           } else {
                                   filteredTasks = append(filteredTasks, task)
                           }
                        }
                    }

                    for _, task := range filteredTasks {
                            task.SetIdPaddingBy(tasks)
                            fmt.Println(task.PrettyPrint(prettyformat))
                    }
                }
            },
        }
        cmdList.Flags().BoolVarP(&numtasks, "num-tasks", "n", false,
                                 "Show the number of tasks")
        cmdList.Flags().BoolVarP(&finished, "finished", "f", false,
                                 "Show finished tasks")
        cmdList.Flags().StringVarP(&sortby, "sort", "s", "prio",
                                   "Sort tasks by parameter (prio|date|len|prio-rev|date-rev|len-rev|id|rand)")
        cmdList.Flags().StringVarP(&prettyformat, "pretty", "", "%i%c %p %t%r",
                                   "Pretty print tasks")
        cmdList.Flags().BoolVarP(&no_color, "no-color", "c", false,
                                 "Do not use colored output when pretty-printing")

        var cmdAdd = &cobra.Command{
            Use:   "add [task]",
            Short: "Adds a task to the todo list.",
            Long:  `Adds a task to the todo list.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                task := strings.Join(args, " ")
                tasks.Add(task)

                tasks.Save(filename)
            },
        }

        var nofinishdate bool
        var cmdDone = &cobra.Command{
            Use:   "done [taskid]",
            Short: "Marks task as done.",
            Long:  `Marks task as done.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what needs to be done?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                err = tasks.Done(taskid, !nofinishdate)
                if err != nil {
                        fmt.Printf("There was an error %v\n", err)
                }

                tasks.Save(filename)
            },
        }
        cmdDone.Flags().BoolVarP(&nofinishdate, "no-finish-date", "D", false,
                                        "Do not mark finished tasks with date.")

        var cmdArchive = &cobra.Command{
            Use:   "archive [taskid]",
            Short: "Archives task.",
            Long:  `Archives task.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what needs to be done?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                fmt.Printf("Archiving task %v\n", taskid)

                tasks.Save(filename)
            },
        }

        var setprio string
        var settodo string
        var cmdSet = &cobra.Command{
            Use:   "set [taskid]",
            Short: "Sets given task's parameters.",
            Long:  `Sets given task's parameters.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what do you want to edit?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                if len(setprio) > 0 {
                        tasks[taskid].SetPriority(setprio[0])
                        tasks[taskid].RebuildRawTodo()
                }

                if len(settodo) > 0 {
                        tasks[taskid].SetTodo(settodo)
                        tasks[taskid].RebuildRawTodo()
                }

                tasks.Save(filename)
            },
        }

        var cmdEdit = &cobra.Command{
            Use:   "edit [taskid]",
            Short: "Edit given task",
            Long:  `Edit given task with your prefered editor.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what do you want to edit?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                text := tasks[taskid].RawText()

                file, err := ioutil.TempFile(os.TempDir(), "gotodo")
                defer os.Remove(file.Name())

                if err != nil {
                        panic(err)
                }

                e := ioutil.WriteFile(file.Name(), []byte(text), 0644)
                if e != nil {
                        panic(e)
                }

                editor := os.Getenv("EDITOR")
                if len(editor) == 0 {
                        editor = "nano" //FIXME: saner default?
                }

                c := exec.Command(editor, file.Name())

                // nasty hack, see http://stackoverflow.com/a/12089980
                c.Stdin = os.Stdin
                c.Stdout = os.Stdout
                c.Stderr = os.Stderr

                er := c.Run()

                if er != nil {
                        fmt.Println(er.Error())
                        panic(er)
                }

                dat, err := ioutil.ReadFile(file.Name())
                if err != nil {
                        panic(err)
                }

                lines := strings.Split(string(dat), "\n")
                if len (lines) > 0 {
                        if tasks[taskid].RawText() != lines[0] {
                                tasks[taskid] = todotxt.ParseTask(lines[0], taskid)
                                fmt.Printf("Task %d updated to:\n%s\n", taskid, lines[0])
                        }
                }

                tasks.Save(filename)
            },
        }

        cmdSet.PersistentFlags().StringVarP(&setprio, "priority", "p", "",
                                     "Sets task's priority.")
        cmdSet.PersistentFlags().StringVarP(&settodo, "todo", "t", "",
                                     "Sets task's todo.")

        var GotodoCmd = &cobra.Command{
            Use:   "gotodo",
            Short: "Gotodo is a go implementation of todo.txt.",
            Long: `A small, fast and fun implementation of todo.txt`,
            Run: func(cmd *cobra.Command, args []string) {
                cmdList.Run(cmd, nil)
            },
        }

        GotodoCmd.PersistentFlags().StringVarP(&filename, "filename", "", "",
                                     "Load tasks from this file.")

        conf.ParseAll()

        // values here
        flags["file"] = *flagFilename

        // sadly, this is the best we can do right now
        if filename == "" {
                if *flagFilename == "" {
                        filename = "todo.txt"
                } else {
                        filename = *flagFilename
                }
        }

        GotodoCmd.AddCommand(cmdList)
        GotodoCmd.AddCommand(cmdAdd)
        GotodoCmd.AddCommand(cmdDone)
        GotodoCmd.AddCommand(cmdArchive)
        GotodoCmd.AddCommand(cmdSet)
        GotodoCmd.AddCommand(cmdEdit)
        GotodoCmd.AddCommand(cmdConfig)
        GotodoCmd.Execute()
}
<|endoftext|>


<commit_before>package main

import  (
        "fmt"
        "../go-todotxt"
        "github.com/docopt/docopt.go"
)

func main() {

        usage := `Go Todo.txt

Usage:
    gotodo [--sort=<prio|date|len|prio-rev|date-rev|len-rev|id>]
    gotodo list [--sort=<prio|date|len|prio-rev|date-rev|len-rev|id>] [<keyword>]
    gotodo add <task>
    gotodo (finish|done) <id>
    gotodo --num-tasks
    gotodo -h | --help
    gotodo -v | --version

Options:
    -h --help     Show this screen.
    -v --version  Show version.
    --num-tasks   Show number of tasks.`

        arguments, _ := docopt.Parse(usage, nil, true, "Go Todo.txt 0.1", false)

        //fmt.Println(arguments)

        tasks := todotxt.LoadTaskList("todo.txt")

        if (arguments["--num-tasks"].(bool)) {
                fmt.Println(tasks.Len())
        } else {

                var by string
                if arguments["--sort"] == nil {
                        by = "prio"
                } else {
                        by = arguments["--sort"].(string)
                }
                tasks.Sort(by)

                for _, task := range tasks {
                        if !task.Finished() {
                                fmt.Println(task.Id(), task.RawText())
                        }
                }
        }

        //fmt.Println(tasks)
        //fmt.Println(tasks.Count())
}
<commit_msg>finished options<commit_after>package main

import  (
        "fmt"
        "../go-todotxt"
        "github.com/docopt/docopt.go"
)

func main() {

        usage := `Go Todo.txt

Usage:
    gotodo [--sort=<prio|date|len|prio-rev|date-rev|len-rev|id>]
    gotodo list [--sort=<prio|date|len|prio-rev|date-rev|len-rev|id>] [<keyword>]
    gotodo add <task>
    gotodo (finish|done) <id>
    gotodo --num-tasks
    gotodo -h | --help
    gotodo -v | --version

Options:
    -h --help     Show this screen.
    -v --version  Show version.
    --num-tasks   Show number of tasks.
    -f --finished Show the finished tasks.`

        arguments, _ := docopt.Parse(usage, nil, true, "Go Todo.txt 0.1", false)

        //fmt.Println(arguments)

        tasks := todotxt.LoadTaskList("todo.txt")

        if (arguments["--num-tasks"].(bool)) {
                fmt.Println(tasks.Len())
        } else {

                var by string
                if arguments["--sort"] == nil {
                        by = "prio"
                } else {
                        by = arguments["--sort"].(string)
                }
                tasks.Sort(by)

                for _, task := range tasks {
                        if !task.Finished() {
                                fmt.Println(task.Id(), task.RawText())
                        }
                }
        }

        //fmt.Println(tasks)
        //fmt.Println(tasks.Count())
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"github.com/labstack/echo"
)

// Group holds the group response from group-store
type Group struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

// Validate the group
func (g *Group) Validate() error {
	if g.Name == "" {
		return errors.New("Group name is empty")
	}

	return nil
}

// Map : maps a group from a request's body and validates the input
func (g *Group) Map(c echo.Context) *echo.HTTPError {
	body := c.Request().Body()
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return ErrBadReqBody
	}

	err = json.Unmarshal(data, &g)
	if err != nil {
		return ErrBadReqBody
	}

	err = g.Validate()
	if err != nil {
		return ErrBadReqBody
	}

	return nil
}

// getGroupsHandler : get all datacenters
func getGroupsHandler(c echo.Context) error {
	msg, err := n.Request("group.find", nil, 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// getGroupHandler : get a group by id
func getGroupHandler(c echo.Context) error {
	var query string
	query = fmt.Sprintf(`{"id": %s}`, c.Param("group"))
	msg, err := n.Request("group.get", []byte(query), 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// createGroupHandler : Endpoint to create a datacenter
func createGroupHandler(c echo.Context) error {
	var g Group

	if authenticatedUser(c).Admin != true {
		return ErrUnauthorized
	}

	if g.Map(c) != nil {
		return ErrBadReqBody
	}

	data, err := json.Marshal(g)
	if err != nil {
		return ErrInternal
	}

	msg, err := n.Request("group.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// updateGroupHandler : Updates a group trough its store
func updateGroupHandler(c echo.Context) error {
	var g Group
	if g.Map(c) != nil {
		return ErrBadReqBody
	}

	au := authenticatedUser(c)
	if au.Admin != true {
		return ErrUnauthorized
	}

	data, err := json.Marshal(g)
	if err != nil {
		return ErrInternal
	}

	msg, err := n.Request("group.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// deleteGroupHandler : Deletes a group though its store
func deleteGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	// Check if there is users on the group
	var users []User
	query := fmt.Sprintf(`{"group_id": %s}`, c.Param("group"))
	msg, err := n.Request("user.find", []byte(query), 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}
	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}
	err = json.Unmarshal(msg.Data, &users)
	if err != nil {
		return ErrInternal
	}

	if len(users) > 0 {
		return ErrInternal
	}

	// Check if there is datacenters on the group
	var datacenters []Datacenter
	query = fmt.Sprintf(`{"group_id": %s}`, c.Param("group"))
	msg, err = n.Request("datacenter.find", []byte(query), 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}
	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}
	err = json.Unmarshal(msg.Data, &datacenters)
	if err != nil {
		return ErrInternal
	}

	if len(datacenters) > 0 {
		return ErrInternal
	}

	query = fmt.Sprintf(`{"id": %s}`, c.Param("group"))
	msg, err = n.Request("group.del", []byte(query), 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.String(http.StatusOK, "")
}

// deleteUserFromGroupHandler : Deletes an user from a group
func deleteUserFromGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	userid, err := strconv.Atoi(c.Param("user"))
	if err != nil {
		return ErrBadReqBody
	}
	udata, err := getUser(userid)
	if err != nil {
		return ErrBadReqBody
	}

	var user User
	err = json.Unmarshal(udata, &user)
	if err != nil {
		return ErrBadReqBody
	}
	user.GroupID = 0
	user.Password = ""
	user.Salt = ""

	data, err := json.Marshal(user)
	if err != nil {
		return ErrBadReqBody
	}

	msg, err := n.Request("user.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, []byte(""))

}

// addUserToGroupHandler : Adds an user to a group
func addUserToGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	groupid, err := strconv.Atoi(c.Param("group"))
	if err != nil {
		return ErrBadReqBody
	}
	_, err = getGroup(groupid)
	if err != nil {
		return ErrBadReqBody
	}

	body := c.Request().Body()
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return ErrBadReqBody
	}

	var payload struct {
		GroupID string `json:"groupid"`
		UserID  string `json:"userid"`
	}
	err = json.Unmarshal(data, &payload)
	if err != nil {
		return ErrBadReqBody
	}

	userid, err := strconv.Atoi(payload.UserID)
	if err != nil {
		return ErrBadReqBody
	}
	udata, err := getUser(userid)
	if err != nil {
		return ErrBadReqBody
	}

	var user User
	err = json.Unmarshal(udata, &user)
	if err != nil {
		return ErrBadReqBody
	}
	user.GroupID = groupid
	user.Password = ""
	user.Salt = ""

	data, err = json.Marshal(user)
	if err != nil {
		return ErrBadReqBody
	}

	msg, err := n.Request("user.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, []byte(""))
}

// addDatacenterToGroupHandler : Adds a datacenter to a group
func addDatacenterToGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	groupid, err := strconv.Atoi(c.Param("group"))
	if err != nil {
		return ErrBadReqBody
	}
	_, err = getGroup(groupid)
	if err != nil {
		return ErrBadReqBody
	}

	body := c.Request().Body()
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return ErrBadReqBody
	}

	var payload struct {
		GroupID      string `json:"groupid"`
		DatacenterID string `json:"datacenterid"`
	}
	err = json.Unmarshal(data, &payload)
	if err != nil {
		return ErrBadReqBody
	}

	datacenterid, err := strconv.Atoi(payload.DatacenterID)
	if err != nil {
		return ErrBadReqBody
	}
	ddata, err := getDatacenterByID(datacenterid)
	if err != nil {
		return ErrBadReqBody
	}

	var datacenter Datacenter
	err = json.Unmarshal(ddata, &datacenter)
	if err != nil {
		return ErrBadReqBody
	}
	datacenter.GroupID = groupid

	data, err = json.Marshal(datacenter)
	if err != nil {
		return ErrBadReqBody
	}

	msg, err := n.Request("datacenter.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, []byte(""))
}

// deleteDatacenterFromGroupHandler : Deletes a datacenter from a group
func deleteDatacenterFromGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	groupid, err := strconv.Atoi(c.Param("group"))
	if err != nil {
		return ErrBadReqBody
	}
	_, err = getGroup(groupid)
	if err != nil {
		return ErrBadReqBody
	}

	datacenterid, err := strconv.Atoi(c.Param("datacenter"))
	if err != nil {
		return ErrBadReqBody
	}
	_, err = getGroup(groupid)
	if err != nil {
		return ErrBadReqBody
	}

	ddata, err := getDatacenterByID(datacenterid)
	if err != nil {
		return ErrBadReqBody
	}

	var datacenter Datacenter
	err = json.Unmarshal(ddata, &datacenter)
	if err != nil {
		return ErrBadReqBody
	}
	datacenter.GroupID = 0

	data, err := json.Marshal(datacenter)
	if err != nil {
		return ErrBadReqBody
	}

	msg, err := n.Request("datacenter.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, []byte(""))
}
<commit_msg>List only authorized groups<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"github.com/labstack/echo"
	"github.com/nats-io/nats"
)

// Group holds the group response from group-store
type Group struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

// Validate the group
func (g *Group) Validate() error {
	if g.Name == "" {
		return errors.New("Group name is empty")
	}

	return nil
}

// Map : maps a group from a request's body and validates the input
func (g *Group) Map(c echo.Context) *echo.HTTPError {
	body := c.Request().Body()
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return ErrBadReqBody
	}

	err = json.Unmarshal(data, &g)
	if err != nil {
		return ErrBadReqBody
	}

	err = g.Validate()
	if err != nil {
		return ErrBadReqBody
	}

	return nil
}

// getGroupsHandler : get all datacenters
func getGroupsHandler(c echo.Context) error {
	var msg *nats.Msg
	var err error

	au := authenticatedUser(c)
	if au.Admin == false {
		if body, err := getGroupByID(au.GroupID); err != nil {
			return err
		} else {
			body := []byte("[" + string(body) + "]")
			return c.JSONBlob(http.StatusOK, body)
		}
	}

	msg, err = n.Request("group.find", nil, 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// getGroupHandler : get a group by id
func getGroupHandler(c echo.Context) error {
	id, _ := strconv.Atoi(c.Param("group"))
	if body, err := getGroupByID(id); err != nil {
		return err
	} else {
		return c.JSONBlob(http.StatusOK, body)
	}
}

func getGroupByID(id int) (body []byte, err error) {
	query := fmt.Sprintf(`{"id": %d}`, id)
	msg, err := n.Request("group.get", []byte(query), 1*time.Second)
	body = msg.Data

	if err != nil {
		return body, ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return body, re.HTTPError
	}

	return body, nil
}

// createGroupHandler : Endpoint to create a datacenter
func createGroupHandler(c echo.Context) error {
	var g Group

	if authenticatedUser(c).Admin != true {
		return ErrUnauthorized
	}

	if g.Map(c) != nil {
		return ErrBadReqBody
	}

	data, err := json.Marshal(g)
	if err != nil {
		return ErrInternal
	}

	msg, err := n.Request("group.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// updateGroupHandler : Updates a group trough its store
func updateGroupHandler(c echo.Context) error {
	var g Group
	if g.Map(c) != nil {
		return ErrBadReqBody
	}

	au := authenticatedUser(c)
	if au.Admin != true {
		return ErrUnauthorized
	}

	data, err := json.Marshal(g)
	if err != nil {
		return ErrInternal
	}

	msg, err := n.Request("group.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, msg.Data)
}

// deleteGroupHandler : Deletes a group though its store
func deleteGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	// Check if there is users on the group
	var users []User
	query := fmt.Sprintf(`{"group_id": %s}`, c.Param("group"))
	msg, err := n.Request("user.find", []byte(query), 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}
	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}
	err = json.Unmarshal(msg.Data, &users)
	if err != nil {
		return ErrInternal
	}

	if len(users) > 0 {
		return ErrInternal
	}

	// Check if there is datacenters on the group
	var datacenters []Datacenter
	query = fmt.Sprintf(`{"group_id": %s}`, c.Param("group"))
	msg, err = n.Request("datacenter.find", []byte(query), 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}
	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}
	err = json.Unmarshal(msg.Data, &datacenters)
	if err != nil {
		return ErrInternal
	}

	if len(datacenters) > 0 {
		return ErrInternal
	}

	query = fmt.Sprintf(`{"id": %s}`, c.Param("group"))
	msg, err = n.Request("group.del", []byte(query), 1*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.String(http.StatusOK, "")
}

// deleteUserFromGroupHandler : Deletes an user from a group
func deleteUserFromGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	userid, err := strconv.Atoi(c.Param("user"))
	if err != nil {
		return ErrBadReqBody
	}
	udata, err := getUser(userid)
	if err != nil {
		return ErrBadReqBody
	}

	var user User
	err = json.Unmarshal(udata, &user)
	if err != nil {
		return ErrBadReqBody
	}
	user.GroupID = 0
	user.Password = ""
	user.Salt = ""

	data, err := json.Marshal(user)
	if err != nil {
		return ErrBadReqBody
	}

	msg, err := n.Request("user.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, []byte(""))

}

// addUserToGroupHandler : Adds an user to a group
func addUserToGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	groupid, err := strconv.Atoi(c.Param("group"))
	if err != nil {
		return ErrBadReqBody
	}
	_, err = getGroup(groupid)
	if err != nil {
		return ErrBadReqBody
	}

	body := c.Request().Body()
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return ErrBadReqBody
	}

	var payload struct {
		GroupID string `json:"groupid"`
		UserID  string `json:"userid"`
	}
	err = json.Unmarshal(data, &payload)
	if err != nil {
		return ErrBadReqBody
	}

	userid, err := strconv.Atoi(payload.UserID)
	if err != nil {
		return ErrBadReqBody
	}
	udata, err := getUser(userid)
	if err != nil {
		return ErrBadReqBody
	}

	var user User
	err = json.Unmarshal(udata, &user)
	if err != nil {
		return ErrBadReqBody
	}
	user.GroupID = groupid
	user.Password = ""
	user.Salt = ""

	data, err = json.Marshal(user)
	if err != nil {
		return ErrBadReqBody
	}

	msg, err := n.Request("user.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, []byte(""))
}

// addDatacenterToGroupHandler : Adds a datacenter to a group
func addDatacenterToGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	groupid, err := strconv.Atoi(c.Param("group"))
	if err != nil {
		return ErrBadReqBody
	}
	_, err = getGroup(groupid)
	if err != nil {
		return ErrBadReqBody
	}

	body := c.Request().Body()
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return ErrBadReqBody
	}

	var payload struct {
		GroupID      string `json:"groupid"`
		DatacenterID string `json:"datacenterid"`
	}
	err = json.Unmarshal(data, &payload)
	if err != nil {
		return ErrBadReqBody
	}

	datacenterid, err := strconv.Atoi(payload.DatacenterID)
	if err != nil {
		return ErrBadReqBody
	}
	ddata, err := getDatacenterByID(datacenterid)
	if err != nil {
		return ErrBadReqBody
	}

	var datacenter Datacenter
	err = json.Unmarshal(ddata, &datacenter)
	if err != nil {
		return ErrBadReqBody
	}
	datacenter.GroupID = groupid

	data, err = json.Marshal(datacenter)
	if err != nil {
		return ErrBadReqBody
	}

	msg, err := n.Request("datacenter.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, []byte(""))
}

// deleteDatacenterFromGroupHandler : Deletes a datacenter from a group
func deleteDatacenterFromGroupHandler(c echo.Context) error {
	au := authenticatedUser(c)

	if au.Admin != true {
		return ErrUnauthorized
	}

	groupid, err := strconv.Atoi(c.Param("group"))
	if err != nil {
		return ErrBadReqBody
	}
	_, err = getGroup(groupid)
	if err != nil {
		return ErrBadReqBody
	}

	datacenterid, err := strconv.Atoi(c.Param("datacenter"))
	if err != nil {
		return ErrBadReqBody
	}
	_, err = getGroup(groupid)
	if err != nil {
		return ErrBadReqBody
	}

	ddata, err := getDatacenterByID(datacenterid)
	if err != nil {
		return ErrBadReqBody
	}

	var datacenter Datacenter
	err = json.Unmarshal(ddata, &datacenter)
	if err != nil {
		return ErrBadReqBody
	}
	datacenter.GroupID = 0

	data, err := json.Marshal(datacenter)
	if err != nil {
		return ErrBadReqBody
	}

	msg, err := n.Request("datacenter.set", data, 5*time.Second)
	if err != nil {
		return ErrGatewayTimeout
	}

	if re := responseErr(msg); re != nil {
		return re.HTTPError
	}

	return c.JSONBlob(http.StatusOK, []byte(""))
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"fmt"
	"strings"

	"github.com/Masterminds/squirrel"
	"github.com/go-gorp/gorp"
	"github.com/loopfz/scecret/utils/sqlgenerator"
)

// Location represents a location the players can visit.
// It is composed of several cards.
type Location struct {
	ID         int64  `json:"id" db:"id"`
	IDScenario int64  `json:"-" db:"id_scenario"`
	Name       string `json:"name" db:"name"`
	Hidden     bool   `json:"json:"hidden" db:"hidden"`
}

// LocationCard represents the cards contained in a location.
// Decoupled from Location to allow things like multiple "A" locations
type LocationCard struct {
	ID         int64  `json:"id" db:"id"`
	IDLocation int64  `json:"id_location" db:"id_location"`
	IDCard     int64  `json:"id_card" db:"id_card"`
	Letter     string `json:"letter" db:"letter"`
}

// Create a location.
func CreateLocation(db *gorp.DbMap, scenar *Scenario, Name string, Hidden bool) (*Location, error) {
	if db == nil || scenar == nil {
		return nil, errors.New("Missing parameters to create location")
	}

	loc := &Location{
		IDScenario: scenar.ID,
		Name:       strings.TrimSpace(Name),
		Hidden:     Hidden,
	}

	err := loc.Valid()
	if err != nil {
		return nil, err
	}

	err = db.Insert(loc)
	if err != nil {
		return nil, err
	}

	return loc, nil
}

// List locations, with filters.
func ListLocations(db *gorp.DbMap, scenar *Scenario) ([]*Location, error) {
	if db == nil {
		return nil, errors.New("Missing db parameter to list locations")
	}

	selector := sqlgenerator.PGsql.Select(`*`).From(`"location"`)

	if scenar != nil {
		selector.Where(squirrel.Eq{`id_scenario`: scenar.ID})
	}

	query, args, err := selector.ToSql()
	if err != nil {
		return nil, err
	}

	var loc []*Location

	_, err = db.Select(&loc, query, args...)
	if err != nil {
		return nil, err
	}

	return loc, nil
}

// Load a Location from ID. Optionally filtered by scenario.
func LoadLocationFromID(db *gorp.DbMap, scenar *Scenario) (*Location, error) {
	if db == nil {
		return nil, errors.New("Missing db parameter to list locations")
	}

	selector := sqlgenerator.PGsql.Select(`*`).From(`"location"`)

	if scenar != nil {
		selector.Where(squirrel.Eq{`id_scenario`: scenar.ID})
	}

	query, args, err := selector.ToSql()
	if err != nil {
		return nil, err
	}

	var loc Location

	err = db.SelectOne(&loc, query, args...)
	if err != nil {
		return nil, err
	}

	return &loc, nil
}

// Update a location.
func (loc *Location) Update(db *gorp.DbMap, Name string, Hidden bool) error {
	if db == nil {
		return errors.New("Missing db parameter to update location")
	}

	loc.Name = Name
	loc.Hidden = Hidden

	// TODO harmonize LocationCards descriptions (new name)

	err := loc.Valid()
	if err != nil {
		return err
	}

	rows, err := db.Update(loc)
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("No such location to update")
	}
	return nil
}

// Delete a location.
func (loc *Location) Delete(db *gorp.DbMap) error {
	if db == nil {
		return errors.New("Missing db parameter to delete location")
	}

	rows, err := db.Delete(loc)
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("No such location to delete")
	}
	return nil
}

// Verify that a Location is valid before creating/updating it.
func (loc *Location) Valid() error {
	if loc.Name == "" {
		return errors.New("Empty location name")
	}
	return nil
}

// Create a link between a card and a location.
func (loc *Location) CreateLocationCard(db *gorp.DbMap, scenar *Scenario, letter string) (*LocationCard, error) {
	if db == nil {
		return nil, errors.New("Missing db parameter to create location card")
	}

	cardDesc := fmt.Sprintf("%s - %s", loc.Name, letter)
	card, err := CreateCard(db, scenar, 0, cardDesc, &CardFace{}, &CardFace{})
	if err != nil {
		return nil, err
	}

	lc := &LocationCard{
		IDCard:     card.ID,
		IDLocation: loc.ID,
		Letter:     strings.ToUpper(letter),
	}

	err = lc.Valid()
	if err != nil {
		return nil, err // TODO Tx
	}

	err = db.Insert(lc)
	if err != nil {
		return nil, err // TODO Tx
	}

	return lc, nil
}

// Verify that a LocationCard is valid before creating/updating it.
func (lc *LocationCard) Valid() error {
	letters := []string{"A", "B", "C", "D", "E", "F", "G", "H"}
	ok := false
	for _, l := range letters {
		if lc.Letter == l {
			ok = true
		}
	}
	if !ok {
		return fmt.Errorf("Invalid location letter: %s", lc.Letter)
	}
	return nil
}

// TEMP
func (loc *Location) GetCards(db *gorp.DbMap) ([]*Card, error) {

	var c []*Card

	_, err := db.Select(&c, `SELECT card.* from "card" JOIN location_card ON card.id = location_card.id_card WHERE location_card.id_location = $1`, loc.ID)
	if err != nil {
		return nil, err
	}

	return c, nil
}
<commit_msg>LocationCard functions, misc fixes<commit_after>package models

import (
	"errors"
	"fmt"
	"strings"

	"github.com/Masterminds/squirrel"
	"github.com/go-gorp/gorp"
	"github.com/loopfz/scecret/utils/sqlgenerator"
)

// Location represents a location the players can visit.
// It is composed of several cards.
type Location struct {
	ID         int64  `json:"id" db:"id"`
	IDScenario int64  `json:"-" db:"id_scenario"`
	Name       string `json:"name" db:"name"`
	Hidden     bool   `json:"json:"hidden" db:"hidden"`
}

// LocationCard represents the cards contained in a location.
// Decoupled from Location to allow things like multiple "A" locations
type LocationCard struct {
	ID         int64  `json:"id" db:"id"`
	IDLocation int64  `json:"id_location" db:"id_location"`
	IDCard     int64  `json:"id_card" db:"id_card"`
	Letter     string `json:"letter" db:"letter"`
}

// Create a location.
func CreateLocation(db *gorp.DbMap, scenar *Scenario, Name string, Hidden bool) (*Location, error) {
	if db == nil || scenar == nil {
		return nil, errors.New("Missing parameters to create location")
	}

	loc := &Location{
		IDScenario: scenar.ID,
		Name:       strings.TrimSpace(Name),
		Hidden:     Hidden,
	}

	err := loc.Valid()
	if err != nil {
		return nil, err
	}

	err = db.Insert(loc)
	if err != nil {
		return nil, err
	}

	return loc, nil
}

// List locations, with filters.
func ListLocations(db *gorp.DbMap, scenar *Scenario) ([]*Location, error) {
	if db == nil {
		return nil, errors.New("Missing db parameter to list locations")
	}

	selector := sqlgenerator.PGsql.Select(`*`).From(`"location"`)

	if scenar != nil {
		selector.Where(squirrel.Eq{`id_scenario`: scenar.ID})
	}

	query, args, err := selector.ToSql()
	if err != nil {
		return nil, err
	}

	var loc []*Location

	_, err = db.Select(&loc, query, args...)
	if err != nil {
		return nil, err
	}

	return loc, nil
}

// Load a Location from ID. Optionally filtered by scenario.
func LoadLocationFromID(db *gorp.DbMap, scenar *Scenario, ID int64) (*Location, error) {
	if db == nil {
		return nil, errors.New("Missing db parameter to list locations")
	}

	selector := sqlgenerator.PGsql.Select(`*`).From(`"location"`).Where(
		squirrel.Eq{`id`: ID},
	)

	if scenar != nil {
		selector.Where(squirrel.Eq{`id_scenario`: scenar.ID})
	}

	query, args, err := selector.ToSql()
	if err != nil {
		return nil, err
	}

	var loc Location

	err = db.SelectOne(&loc, query, args...)
	if err != nil {
		return nil, err
	}

	return &loc, nil
}

// Update a location.
func (loc *Location) Update(db *gorp.DbMap, Name string, Hidden bool) error {
	if db == nil {
		return errors.New("Missing db parameter to update location")
	}

	loc.Name = Name
	loc.Hidden = Hidden

	// TODO harmonize LocationCards descriptions (new name)

	err := loc.Valid()
	if err != nil {
		return err
	}

	rows, err := db.Update(loc)
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("No such location to update")
	}
	return nil
}

// Delete a location.
func (loc *Location) Delete(db *gorp.DbMap) error {
	if db == nil {
		return errors.New("Missing db parameter to delete location")
	}

	rows, err := db.Delete(loc)
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("No such location to delete")
	}
	return nil
}

// Verify that a Location is valid before creating/updating it.
func (loc *Location) Valid() error {
	if loc.Name == "" {
		return errors.New("Empty location name")
	}
	return nil
}

// Create a link between a card and a location.
func (loc *Location) CreateLocationCard(db *gorp.DbMap, scenar *Scenario, letter string) (*LocationCard, error) {
	if db == nil {
		return nil, errors.New("Missing db parameter to create location card")
	}

	letter = strings.ToUpper(strings.TrimSpace(letter))

	cardDesc := fmt.Sprintf("%s - %s", loc.Name, letter)
	card, err := CreateCard(db, scenar, 0, cardDesc, &CardFace{}, &CardFace{})
	if err != nil {
		return nil, err
	}

	lc := &LocationCard{
		IDCard:     card.ID,
		IDLocation: loc.ID,
		Letter:     letter,
	}

	err = lc.Valid()
	if err != nil {
		return nil, err // TODO Tx
	}

	err = db.Insert(lc)
	if err != nil {
		return nil, err // TODO Tx
	}

	return lc, nil
}

// List a location's cards.
func (loc *Location) ListLocationCards(db *gorp.DbMap) ([]*LocationCard, error) {
	if db == nil {
		return nil, errors.New("Missing db parameter to list location cards")
	}

	query, args, err := sqlgenerator.PGsql.Select(`*`).From(`"location_card"`).Where(
		squirrel.Eq{`id_location`: loc.ID},
	).ToSql()

	if err != nil {
		return nil, err
	}

	var lc []*LocationCard

	_, err = db.Select(&lc, query, args...)
	if err != nil {
		return nil, err
	}

	return lc, nil
}

// Load a location card,
func (loc *Location) LoadLocationCardFromID(db *gorp.DbMap, ID int64) (*LocationCard, error) {
	if db == nil {
		return nil, errors.New("Missing db parameter to load location card")
	}

	query, args, err := sqlgenerator.PGsql.Select(`*`).From(`"location_card"`).Where(
		squirrel.And{
			squirrel.Eq{`id`: ID},
			squirrel.Eq{`id_location`: loc.ID},
		},
	).ToSql()

	if err != nil {
		return nil, err
	}

	var lc LocationCard

	_, err = db.Select(&lc, query, args...)
	if err != nil {
		return nil, err
	}

	return &lc, nil
}

// Update a location card.
func (lc *LocationCard) Update(db *gorp.DbMap, letter string) error {
	if db == nil {
		return errors.New("Missing db parameter to update location card")
	}

	lc.Letter = strings.ToUpper(strings.TrimSpace(letter))

	err := lc.Valid()
	if err != nil {
		return err
	}

	// Update card description with new letter
	loc, err := LoadLocationFromID(db, nil, lc.IDLocation)
	if err != nil {
		return err
	}
	card, err := LoadCardFromID(db, nil, lc.IDCard)
	if err != nil {
		return err
	}
	cardDesc := fmt.Sprintf("%s - %s", loc.Name, lc.Letter)
	err = card.Update(db, card.Number, cardDesc, card.Front, card.Back)
	if err != nil {
		return err
	}

	rows, err := db.Update(lc)
	if err != nil {
		return err // TODO Tx
	}
	if rows == 0 {
		return errors.New("No such location card to update") // TODO Tx
	}

	return nil
}

// Delete a location card.
func (lc *LocationCard) Delete(db *gorp.DbMap) error {
	if db == nil {
		return errors.New("Missing db parameter to update location card")
	}

	// Delete card object
	card, err := LoadCardFromID(db, nil, lc.IDCard)
	if err != nil {
		return err
	}
	err = card.Delete(db)
	if err != nil {
		return err
	}

	rows, err := db.Delete(lc)
	if err != nil {
		return err // TODO Tx
	}
	if rows == 0 {
		return errors.New("No such location card to delete") // TODO Tx
	}

	return nil
}

// Verify that a LocationCard is valid before creating/updating it.
func (lc *LocationCard) Valid() error {
	letters := []string{"A", "B", "C", "D", "E", "F", "G", "H"}
	ok := false
	for _, l := range letters {
		if lc.Letter == l {
			ok = true
		}
	}
	if !ok {
		return fmt.Errorf("Invalid location letter: %s", lc.Letter)
	}
	return nil
}

// TEMP ?
func (loc *Location) GetCards(db *gorp.DbMap) ([]*Card, error) {

	var c []*Card

	_, err := db.Select(&c, `SELECT card.* from "card" JOIN location_card ON card.id = location_card.id_card WHERE location_card.id_location = $1`, loc.ID)
	if err != nil {
		return nil, err
	}

	return c, nil
}
<|endoftext|>


<commit_before>// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers.
//
// As a reminder from https://golang.org/pkg/crypto/tls/#Config:
//	A Config structure is used to configure a TLS client or server. After one has been passed to a TLS function it must not be modified.
//	A Config may be reused; the tls package will also not modify it.
package tlsconfig

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/Sirupsen/logrus"
)

// Options represents the information needed to create client and server TLS configurations.
type Options struct {
	CAFile string

	// If either CertFile or KeyFile is empty, Client() will not load them
	// preventing the client from authenticating to the server.
	// However, Server() requires them and will error out if they are empty.
	CertFile string
	KeyFile  string

	// client-only option
	InsecureSkipVerify bool
	// server-only option
	ClientAuth tls.ClientAuthType
}

// Extra (server-side) accepted CBC cipher suites - will phase out in the future
var acceptedCBCCiphers = []uint16{
	tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
	tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
	tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
	tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
	tls.TLS_RSA_WITH_AES_256_CBC_SHA,
	tls.TLS_RSA_WITH_AES_128_CBC_SHA,
}

// DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls
// options struct but wants to use a commonly accepted set of TLS cipher suites, with
// known weak algorithms removed.
var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...)

// ServerDefault is a secure-enough TLS configuration for the server TLS configuration.
var ServerDefault = tls.Config{
	// Avoid fallback to SSL protocols < TLS1.0
	MinVersion:               tls.VersionTLS10,
	PreferServerCipherSuites: true,
	CipherSuites:             DefaultServerAcceptedCiphers,
}

// ClientDefault is a secure-enough TLS configuration for the client TLS configuration.
var ClientDefault = tls.Config{
	// Prefer TLS1.2 as the client minimum
	MinVersion:   tls.VersionTLS12,
	CipherSuites: clientCipherSuites,
}

// certPool returns an X.509 certificate pool from `caFile`, the certificate file.
func certPool(caFile string) (*x509.CertPool, error) {
	// If we should verify the server, we need to load a trusted ca
	certPool := x509.NewCertPool()
	pem, err := ioutil.ReadFile(caFile)
	if err != nil {
		return nil, fmt.Errorf("Could not read CA certificate %q: %v", caFile, err)
	}
	if !certPool.AppendCertsFromPEM(pem) {
		return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile)
	}
	s := certPool.Subjects()
	subjects := make([]string, len(s))
	for i, subject := range s {
		subjects[i] = string(subject)
	}
	logrus.Debugf("Trusting certs with subjects: %v", subjects)
	return certPool, nil
}

// Client returns a TLS configuration meant to be used by a client.
func Client(options Options) (*tls.Config, error) {
	tlsConfig := ClientDefault
	tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
	if !options.InsecureSkipVerify && options.CAFile != "" {
		CAs, err := certPool(options.CAFile)
		if err != nil {
			return nil, err
		}
		tlsConfig.RootCAs = CAs
	}

	if options.CertFile != "" || options.KeyFile != "" {
		tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
		if err != nil {
			return nil, fmt.Errorf("Could not load X509 key pair: %v. Make sure the key is not encrypted", err)
		}
		tlsConfig.Certificates = []tls.Certificate{tlsCert}
	}

	return &tlsConfig, nil
}

// Server returns a TLS configuration meant to be used by a server.
func Server(options Options) (*tls.Config, error) {
	tlsConfig := ServerDefault
	tlsConfig.ClientAuth = options.ClientAuth
	tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.CertFile, options.KeyFile, err)
		}
		return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err)
	}
	tlsConfig.Certificates = []tls.Certificate{tlsCert}
	if options.ClientAuth >= tls.VerifyClientCertIfGiven {
		CAs, err := certPool(options.CAFile)
		if err != nil {
			return nil, err
		}
		tlsConfig.ClientCAs = CAs
	}
	return &tlsConfig, nil
}
<commit_msg>Debug log just the number of certificates, instead of raw subject bytes<commit_after>// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers.
//
// As a reminder from https://golang.org/pkg/crypto/tls/#Config:
//	A Config structure is used to configure a TLS client or server. After one has been passed to a TLS function it must not be modified.
//	A Config may be reused; the tls package will also not modify it.
package tlsconfig

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/Sirupsen/logrus"
)

// Options represents the information needed to create client and server TLS configurations.
type Options struct {
	CAFile string

	// If either CertFile or KeyFile is empty, Client() will not load them
	// preventing the client from authenticating to the server.
	// However, Server() requires them and will error out if they are empty.
	CertFile string
	KeyFile  string

	// client-only option
	InsecureSkipVerify bool
	// server-only option
	ClientAuth tls.ClientAuthType
}

// Extra (server-side) accepted CBC cipher suites - will phase out in the future
var acceptedCBCCiphers = []uint16{
	tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
	tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
	tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
	tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
	tls.TLS_RSA_WITH_AES_256_CBC_SHA,
	tls.TLS_RSA_WITH_AES_128_CBC_SHA,
}

// DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls
// options struct but wants to use a commonly accepted set of TLS cipher suites, with
// known weak algorithms removed.
var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...)

// ServerDefault is a secure-enough TLS configuration for the server TLS configuration.
var ServerDefault = tls.Config{
	// Avoid fallback to SSL protocols < TLS1.0
	MinVersion:               tls.VersionTLS10,
	PreferServerCipherSuites: true,
	CipherSuites:             DefaultServerAcceptedCiphers,
}

// ClientDefault is a secure-enough TLS configuration for the client TLS configuration.
var ClientDefault = tls.Config{
	// Prefer TLS1.2 as the client minimum
	MinVersion:   tls.VersionTLS12,
	CipherSuites: clientCipherSuites,
}

// certPool returns an X.509 certificate pool from `caFile`, the certificate file.
func certPool(caFile string) (*x509.CertPool, error) {
	// If we should verify the server, we need to load a trusted ca
	certPool := x509.NewCertPool()
	pem, err := ioutil.ReadFile(caFile)
	if err != nil {
		return nil, fmt.Errorf("Could not read CA certificate %q: %v", caFile, err)
	}
	if !certPool.AppendCertsFromPEM(pem) {
		return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile)
	}
	logrus.Debugf("Trusting %d certs", len(certPool.Subjects()))
	return certPool, nil
}

// Client returns a TLS configuration meant to be used by a client.
func Client(options Options) (*tls.Config, error) {
	tlsConfig := ClientDefault
	tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
	if !options.InsecureSkipVerify && options.CAFile != "" {
		CAs, err := certPool(options.CAFile)
		if err != nil {
			return nil, err
		}
		tlsConfig.RootCAs = CAs
	}

	if options.CertFile != "" || options.KeyFile != "" {
		tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
		if err != nil {
			return nil, fmt.Errorf("Could not load X509 key pair: %v. Make sure the key is not encrypted", err)
		}
		tlsConfig.Certificates = []tls.Certificate{tlsCert}
	}

	return &tlsConfig, nil
}

// Server returns a TLS configuration meant to be used by a server.
func Server(options Options) (*tls.Config, error) {
	tlsConfig := ServerDefault
	tlsConfig.ClientAuth = options.ClientAuth
	tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.CertFile, options.KeyFile, err)
		}
		return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err)
	}
	tlsConfig.Certificates = []tls.Certificate{tlsCert}
	if options.ClientAuth >= tls.VerifyClientCertIfGiven {
		CAs, err := certPool(options.CAFile)
		if err != nil {
			return nil, err
		}
		tlsConfig.ClientCAs = CAs
	}
	return &tlsConfig, nil
}
<|endoftext|>


<commit_before>package elastic

import (
	"encoding/json"
	"testing"
)

func TestFunctionScoreQuery(t *testing.T) {
	q := NewFunctionScoreQuery().
		Query(NewTermQuery("name.last", "banon")).
		Add(NewTermFilter("name.last", "banon"), NewFactorFunction().BoostFactor(3)).
		AddScoreFunc(NewFactorFunction().BoostFactor(3)).
		AddScoreFunc(NewFactorFunction().BoostFactor(3)).
		Boost(3).
		MaxBoost(10).
		ScoreMode("avg")
	data, err := json.Marshal(q.Source())
	if err != nil {
		t.Fatalf("marshaling to JSON failed: %v", err)
	}
	got := string(data)
	expected := `{"function_score":{"boost":3,"functions":[{"boost_factor":3,"filter":{"term":{"name.last":"banon"}}},{"boost_factor":3},{"boost_factor":3}],"max_boost":10,"query":{"term":{"name.last":"banon"}},"score_mode":"avg"}}`
	if got != expected {
		t.Errorf("expected\n%s\n,got:\n%s", expected, got)
	}
}

func TestFunctionScoreQueryWithNilFilter(t *testing.T) {
	q := NewFunctionScoreQuery().
		Query(NewTermQuery("tag", "wow")).
		AddScoreFunc(NewRandomFunction()).
		Boost(2.0).
		MaxBoost(12.0).
		BoostMode("multiply").
		ScoreMode("max")
	data, err := json.Marshal(q.Source())
	if err != nil {
		t.Fatalf("marshaling to JSON failed: %v", err)
	}
	got := string(data)
	expected := `{"function_score":{"boost":2,"boost_mode":"multiply","max_boost":12,"query":{"term":{"tag":"wow"}},"random_score":{},"score_mode":"max"}}`
	if got != expected {
		t.Errorf("expected\n%s\n,got:\n%s", expected, got)
	}
}

func TestFunctionScoreQueryWithGaussScoreFunc(t *testing.T) {
	q := NewFunctionScoreQuery().
		Query(NewTermQuery("name.last", "banon")).
		AddScoreFunc(NewGaussDecayFunction().FieldName("pin.location").Origin("11, 12").Scale("2km").Offset("0km").Decay(0.33))
	data, err := json.Marshal(q.Source())
	if err != nil {
		t.Fatalf("marshaling to JSON failed: %v", err)
	}
	got := string(data)
	expected := `{"function_score":{"gauss":{"pin.location":{"decay":0.33,"offset":"0km","origin":"11, 12","scale":"2km"}},"query":{"term":{"name.last":"banon"}}}}`
	if got != expected {
		t.Errorf("expected\n%s\n,got:\n%s", expected, got)
	}
}
<commit_msg>Add unit tests for field value factor<commit_after>package elastic

import (
	"encoding/json"
	"testing"
)

func TestFunctionScoreQuery(t *testing.T) {
	q := NewFunctionScoreQuery().
		Query(NewTermQuery("name.last", "banon")).
		Add(NewTermFilter("name.last", "banon"), NewFactorFunction().BoostFactor(3)).
		AddScoreFunc(NewFactorFunction().BoostFactor(3)).
		AddScoreFunc(NewFactorFunction().BoostFactor(3)).
		Boost(3).
		MaxBoost(10).
		ScoreMode("avg")
	data, err := json.Marshal(q.Source())
	if err != nil {
		t.Fatalf("marshaling to JSON failed: %v", err)
	}
	got := string(data)
	expected := `{"function_score":{"boost":3,"functions":[{"boost_factor":3,"filter":{"term":{"name.last":"banon"}}},{"boost_factor":3},{"boost_factor":3}],"max_boost":10,"query":{"term":{"name.last":"banon"}},"score_mode":"avg"}}`
	if got != expected {
		t.Errorf("expected\n%s\n,got:\n%s", expected, got)
	}
}

func TestFunctionScoreQueryWithNilFilter(t *testing.T) {
	q := NewFunctionScoreQuery().
		Query(NewTermQuery("tag", "wow")).
		AddScoreFunc(NewRandomFunction()).
		Boost(2.0).
		MaxBoost(12.0).
		BoostMode("multiply").
		ScoreMode("max")
	data, err := json.Marshal(q.Source())
	if err != nil {
		t.Fatalf("marshaling to JSON failed: %v", err)
	}
	got := string(data)
	expected := `{"function_score":{"boost":2,"boost_mode":"multiply","max_boost":12,"query":{"term":{"tag":"wow"}},"random_score":{},"score_mode":"max"}}`
	if got != expected {
		t.Errorf("expected\n%s\n,got:\n%s", expected, got)
	}
}

func TestFieldValueFactor(t *testing.T) {
	q := NewFunctionScoreQuery().
		Query(NewTermQuery("name.last", "banon")).
		AddScoreFunc(NewFieldValueFactorFunction().Modifier("sqrt").BoostFactor(2).Field("income")).
		Boost(2.0).
		MaxBoost(12.0).
		BoostMode("multiply").
		ScoreMode("max")
	data, err := json.Marshal(q.Source())
	if err != nil {
		t.Fatalf("marshaling to JSON failed: %v", err)
	}
	got := string(data)
	expected := `{"function_score":{"boost":2,"boost_mode":"multiply","field_value_factor":{"factor":2,"field":"income","modifier":"sqrt"},"max_boost":12,"query":{"term":{"name.last":"banon"}},"score_mode":"max"}}`
	if got != expected {
		t.Errorf("expected\n%s\n,got:\n%s", expected, got)
	}
}

func TestFunctionScoreQueryWithGaussScoreFunc(t *testing.T) {
	q := NewFunctionScoreQuery().
		Query(NewTermQuery("name.last", "banon")).
		AddScoreFunc(NewGaussDecayFunction().FieldName("pin.location").Origin("11, 12").Scale("2km").Offset("0km").Decay(0.33))
	data, err := json.Marshal(q.Source())
	if err != nil {
		t.Fatalf("marshaling to JSON failed: %v", err)
	}
	got := string(data)
	expected := `{"function_score":{"gauss":{"pin.location":{"decay":0.33,"offset":"0km","origin":"11, 12","scale":"2km"}},"query":{"term":{"name.last":"banon"}}}}`
	if got != expected {
		t.Errorf("expected\n%s\n,got:\n%s", expected, got)
	}
}
<|endoftext|>


<commit_before>package graphs

import (
	"container/list"
	"errors"
)

var ErrNoDAG = errors.New("graphs: graph is not a DAG")

func initInEdges(g *Graph) map[Vertex]int {
	inEdges := make(map[Vertex]int)
	for e := range g.EdgesIter() {
		if _, ok := inEdges[e.Start]; !ok {
			inEdges[e.Start] = 0
		}
		if _, ok := inEdges[e.End]; ok {
			inEdges[e.End]++
		} else {
			inEdges[e.End] = 1
		}
	}
	return inEdges
}

func removeEdgesFromVertex(v Vertex, g *Graph, inEdges map[Vertex]int) {
	for outEdge := range g.HalfedgesIter(v) {
		neighbor := outEdge.End
		inEdges[neighbor]--
	}
}

func TopologicalSort(g *Graph) (topologicalOrder *list.List, topologicalClasses map[Vertex]int, err error) {
	inEdges := initInEdges(g)

	topologicalClasses = make(map[Vertex]int)
	topologicalOrder = list.New()
	tClass := 0
	for len(inEdges) > 0 {
		topClass := []Vertex{}
		for v, inDegree := range inEdges {
			if inDegree == 0 {
				topClass = append(topClass, v)
				topologicalClasses[v] = tClass
			}
		}
		if len(topClass) == 0 {
			err = ErrNoDAG
			topologicalClasses = make(map[Vertex]int)
			topologicalOrder = list.New()
			return
		}
		for _, v := range topClass {
			removeEdgesFromVertex(v, g, inEdges)
			delete(inEdges, v)
			topologicalOrder.PushBack(v)
		}
		tClass++
	}

	return
}
<commit_msg>Make helper function a closure<commit_after>package graphs

import (
	"container/list"
	"errors"
)

var ErrNoDAG = errors.New("graphs: graph is not a DAG")

func initInEdges(g *Graph) map[Vertex]int {
	inEdges := make(map[Vertex]int)
	for e := range g.EdgesIter() {
		if _, ok := inEdges[e.Start]; !ok {
			inEdges[e.Start] = 0
		}
		if _, ok := inEdges[e.End]; ok {
			inEdges[e.End]++
		} else {
			inEdges[e.End] = 1
		}
	}
	return inEdges
}

func TopologicalSort(g *Graph) (topologicalOrder *list.List, topologicalClasses map[Vertex]int, err error) {
	inEdges := initInEdges(g)

	removeEdgesFromVertex := func(v Vertex) {
		for outEdge := range g.HalfedgesIter(v) {
			neighbor := outEdge.End
			inEdges[neighbor]--
		}
	}

	topologicalClasses = make(map[Vertex]int)
	topologicalOrder = list.New()
	tClass := 0
	for len(inEdges) > 0 {
		topClass := []Vertex{}
		for v, inDegree := range inEdges {
			if inDegree == 0 {
				topClass = append(topClass, v)
				topologicalClasses[v] = tClass
			}
		}
		if len(topClass) == 0 {
			err = ErrNoDAG
			topologicalClasses = make(map[Vertex]int)
			topologicalOrder = list.New()
			return
		}
		for _, v := range topClass {
			removeEdgesFromVertex(v)
			delete(inEdges, v)
			topologicalOrder.PushBack(v)
		}
		tClass++
	}

	return
}
<|endoftext|>


<commit_before><commit_msg>Apparently we do need locking here.<commit_after><|endoftext|>


<commit_before><commit_msg>vcs: Add support for working with github.com/sourcegraph/go-vcs.<commit_after><|endoftext|>


<commit_before><commit_msg>:+1: Bump up the version<commit_after><|endoftext|>


<commit_before><commit_msg>version: bump master back to -dev<commit_after><|endoftext|>


<commit_before><commit_msg>Not watch empty path<commit_after><|endoftext|>


<commit_before>package rss

import (
	"fmt"
	"strings"

	"github.com/PuerkitoBio/goquery"
	log "github.com/Sirupsen/logrus"
)

const (
	// MaxDescriptionWords is the maximum number of words in a description
	MaxDescriptionWords = 50
)

func getImageRss1(item rss1_0Item) string {
	for _, enc := range item.Enclosures {
		if strings.Contains(enc.Type, "image") {
			return enc.Url
		}
	}

	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse url %v", item.Link)
		return ""
	}

	selection := doc.Find(`meta[property="og:image"]`)
	if selection != nil {
		if imgURL, found := selection.Attr("content"); found {
			return imgURL
		}
	}

	selection = doc.Find(`meta[property="twitter:image"]`)
	if selection != nil {
		if imgURL, found := selection.Attr("content"); found {
			return imgURL
		}
	}

	return ""
}

func getSummaryRss1(item rss1_0Item) string {
	var fbDesc, twDesc, socDesc, rssDesc string
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse url %v", item.Link)
		return ""
	}
	selection := doc.Find(`meta[property="og:description"]`)
	if selection != nil {
		if desc, found := selection.Attr("content"); found {
			fbDesc = desc
		}
	}

	selection = doc.Find(`meta[property="twitter:description"]`)
	if selection != nil {
		if desc, found := selection.Attr("content"); found {
			twDesc = desc
		}
	}

	if !strings.ContainsAny(item.Description, "<>") {
		rssDesc = item.Description
	}

	if len(fbDesc) >= len(twDesc) {
		socDesc = fbDesc
	} else {
		socDesc = twDesc
	}

	var desc string

	if len(socDesc) >= len(rssDesc) {
		desc = socDesc
	} else {
		desc = rssDesc
	}

	descSlice := strings.Split(desc, " ")

	if len(descSlice) > MaxDescriptionWords {
		desc = strings.Join(descSlice[:MaxDescriptionWords], " ")
	}
	if desc != "" {
		desc = fmt.Sprintf("%s...", desc)
	}
	return desc
}

func getTitleRss1(item rss1_0Item) string {
	var fbTitle, twTitle, socTitle, rssTitle string
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse url %v", item.Link)
		return ""
	}
	selection := doc.Find(`meta[property="og:title"]`)
	if selection != nil {
		if t, found := selection.Attr("content"); found {
			fbTitle = t
		}
	}

	selection = doc.Find(`meta[property="twitter:title"]`)
	if selection != nil {
		if t, found := selection.Attr("content"); found {
			twTitle = t
		}
	}

	rssTitle = item.Title

	if len(fbTitle) >= len(twTitle) {
		socTitle = fbTitle
	} else {
		socTitle = twTitle
	}

	var title string

	if len(socTitle) >= len(rssTitle) {
		title = socTitle
	} else {
		title = rssTitle
	}
	return title
}

func getImageRss2(item rss2_0Item) string {
	for _, enc := range item.Enclosures {
		if strings.Contains(enc.Type, "image") {
			return enc.Url
		}
	}
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse url %v", item.Link)
		return ""
	}

	selection := doc.Find(`meta[property="og:image"]`)
	if selection != nil {
		if imgURL, found := selection.Attr("content"); found {
			return imgURL
		}
	}

	selection = doc.Find(`meta[property="twitter:image"]`)
	if selection != nil {
		if imgURL, found := selection.Attr("content"); found {
			return imgURL
		}
	}

	return ""
}

func getSummaryRss2(item rss2_0Item) string {
	var fbDesc, twDesc, socDesc, rssDesc string
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse url %v", item.Link)
		return ""
	}
	selection := doc.Find(`meta[property="og:description"]`)
	if selection != nil {
		if desc, found := selection.Attr("content"); found {
			fbDesc = desc
		}
	}

	selection = doc.Find(`meta[property="twitter:description"]`)
	if selection != nil {
		if desc, found := selection.Attr("content"); found {
			twDesc = desc
		}
	}

	if !strings.ContainsAny(item.Description, "<>") {
		rssDesc = item.Description
	}

	if len(fbDesc) >= len(twDesc) {
		socDesc = fbDesc
	} else {
		socDesc = twDesc
	}

	var desc string

	if len(socDesc) >= len(rssDesc) {
		desc = socDesc
	} else {
		desc = rssDesc
	}

	descSlice := strings.Split(desc, " ")

	if len(descSlice) > MaxDescriptionWords {
		desc = strings.Join(descSlice[:MaxDescriptionWords], " ")
	}
	if desc != "" {
		desc = fmt.Sprintf("%s...", desc)
	}
	return desc
}

func getTitleRss2(item rss2_0Item) string {
	var fbTitle, twTitle, socTitle, rssTitle string
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse url %v", item.Link)
		return ""
	}
	selection := doc.Find(`meta[property="og:title"]`)
	if selection != nil {
		if t, found := selection.Attr("content"); found {
			fbTitle = t
		}
	}

	selection = doc.Find(`meta[property="twitter:title"]`)
	if selection != nil {
		if t, found := selection.Attr("content"); found {
			twTitle = t
		}
	}

	rssTitle = item.Title

	if len(fbTitle) >= len(twTitle) {
		socTitle = fbTitle
	} else {
		socTitle = twTitle
	}

	var title string

	if len(socTitle) >= len(rssTitle) {
		title = socTitle
	} else {
		title = rssTitle
	}
	return title
}
<commit_msg>modification to helper.go<commit_after>package rss

import (
	"fmt"
	"strings"

	"github.com/PuerkitoBio/goquery"
	log "github.com/Sirupsen/logrus"
)

const (
	// MaxDescriptionWords is the maximum number of words in a description
	MaxDescriptionWords = 50
)

func getImageRss1(item rss1_0Item) string {
	for _, enc := range item.Enclosures {
		if strings.Contains(enc.Type, "image") {
			return enc.Url
		}
	}

	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse (url %v, error: %v)", item.Link, err.Error())
		return ""
	}

	selection := doc.Find(`meta[property="og:image"]`)
	if selection != nil {
		if imgURL, found := selection.Attr("content"); found {
			return imgURL
		}
	}

	selection = doc.Find(`meta[property="twitter:image"]`)
	if selection != nil {
		if imgURL, found := selection.Attr("content"); found {
			return imgURL
		}
	}

	return ""
}

func getSummaryRss1(item rss1_0Item) string {
	var fbDesc, twDesc, socDesc, rssDesc string
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse (url %v, error: %v)", item.Link, err.Error())
		return item.Description
	}
	selection := doc.Find(`meta[property="og:description"]`)
	if selection != nil {
		if desc, found := selection.Attr("content"); found {
			fbDesc = desc
		}
	}

	selection = doc.Find(`meta[property="twitter:description"]`)
	if selection != nil {
		if desc, found := selection.Attr("content"); found {
			twDesc = desc
		}
	}

	if !strings.ContainsAny(item.Description, "<>") {
		rssDesc = item.Description
	}

	if len(fbDesc) >= len(twDesc) {
		socDesc = fbDesc
	} else {
		socDesc = twDesc
	}

	var desc string

	if len(socDesc) >= len(rssDesc) {
		desc = socDesc
	} else {
		desc = rssDesc
	}

	descSlice := strings.Split(desc, " ")

	if len(descSlice) > MaxDescriptionWords {
		desc = strings.Join(descSlice[:MaxDescriptionWords], " ")
	}
	if desc != "" {
		desc = fmt.Sprintf("%s...", desc)
	}
	return desc
}

func getTitleRss1(item rss1_0Item) string {
	var fbTitle, twTitle, socTitle, rssTitle string
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse (url %v, error: %v)", item.Link, err.Error())
		return item.Title
	}
	selection := doc.Find(`meta[property="og:title"]`)
	if selection != nil {
		if t, found := selection.Attr("content"); found {
			fbTitle = t
		}
	}

	selection = doc.Find(`meta[property="twitter:title"]`)
	if selection != nil {
		if t, found := selection.Attr("content"); found {
			twTitle = t
		}
	}

	rssTitle = item.Title

	if len(fbTitle) >= len(twTitle) {
		socTitle = fbTitle
	} else {
		socTitle = twTitle
	}

	var title string

	if len(socTitle) >= len(rssTitle) {
		title = socTitle
	} else {
		title = rssTitle
	}
	return title
}

func getImageRss2(item rss2_0Item) string {
	for _, enc := range item.Enclosures {
		if strings.Contains(enc.Type, "image") {
			return enc.Url
		}
	}
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse (url %v, error: %v)", item.Link, err.Error())
		return ""
	}

	selection := doc.Find(`meta[property="og:image"]`)
	if selection != nil {
		if imgURL, found := selection.Attr("content"); found {
			return imgURL
		}
	}

	selection = doc.Find(`meta[property="twitter:image"]`)
	if selection != nil {
		if imgURL, found := selection.Attr("content"); found {
			return imgURL
		}
	}

	return ""
}

func getSummaryRss2(item rss2_0Item) string {
	var fbDesc, twDesc, socDesc, rssDesc string
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse (url %v, error: %v)", item.Link, err.Error())
		return item.Description
	}
	selection := doc.Find(`meta[property="og:description"]`)
	if selection != nil {
		if desc, found := selection.Attr("content"); found {
			fbDesc = desc
		}
	}

	selection = doc.Find(`meta[property="twitter:description"]`)
	if selection != nil {
		if desc, found := selection.Attr("content"); found {
			twDesc = desc
		}
	}

	if !strings.ContainsAny(item.Description, "<>") {
		rssDesc = item.Description
	}

	if len(fbDesc) >= len(twDesc) {
		socDesc = fbDesc
	} else {
		socDesc = twDesc
	}

	var desc string

	if len(socDesc) >= len(rssDesc) {
		desc = socDesc
	} else {
		desc = rssDesc
	}

	descSlice := strings.Split(desc, " ")

	if len(descSlice) > MaxDescriptionWords {
		desc = strings.Join(descSlice[:MaxDescriptionWords], " ")
	}
	if desc != "" {
		desc = fmt.Sprintf("%s...", desc)
	}
	return desc
}

func getTitleRss2(item rss2_0Item) string {
	var fbTitle, twTitle, socTitle, rssTitle string
	doc, err := goquery.NewDocument(item.Link)
	if err != nil {
		log.Warnf("can not parse (url %v, error: %v)", item.Link, err.Error())
		return item.Title
	}
	selection := doc.Find(`meta[property="og:title"]`)
	if selection != nil {
		if t, found := selection.Attr("content"); found {
			fbTitle = t
		}
	}

	selection = doc.Find(`meta[property="twitter:title"]`)
	if selection != nil {
		if t, found := selection.Attr("content"); found {
			twTitle = t
		}
	}

	rssTitle = item.Title

	if len(fbTitle) >= len(twTitle) {
		socTitle = fbTitle
	} else {
		socTitle = twTitle
	}

	var title string

	if len(socTitle) >= len(rssTitle) {
		title = socTitle
	} else {
		title = rssTitle
	}
	return title
}
<|endoftext|>


<commit_before>package nakadi

import (
	"encoding/json"
	"net"
	"net/http"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/pkg/errors"
)

const (
	// defaults used by http.DefaultTransport
	defaultKeepAlive        = 30 * time.Second
	defaultIdleConnTimeout  = 90 * time.Second
	// nakadi specific timeouts
	nakadiHeartbeatInterval = 30 * time.Second
)

// newHTTPClient crates an http client which is used for non streaming requests.
func newHTTPClient(timeout time.Duration) *http.Client {
	return &http.Client{
		Timeout: timeout,
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   timeout,
				KeepAlive: defaultKeepAlive,
				DualStack: true,
			}).DialContext,
			MaxIdleConns:        100,
			IdleConnTimeout:     defaultIdleConnTimeout,
			TLSHandshakeTimeout: timeout,
		},
	}
}

// newHTTPStream creates an http client which is used for streaming purposes.
func newHTTPStream(timeout time.Duration) *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   timeout,
				DualStack: true,
			}).DialContext,
			MaxIdleConns:        100,
			IdleConnTimeout:     2 * nakadiHeartbeatInterval,
			TLSHandshakeTimeout: timeout,
		},
	}
}

// problemJSON is used to decode error responses.
type problemJSON struct {
	Title  string `json:"title"`
	Detail string `json:"detail"`
	Status int    `json:"status"`
	Type   string `json:"type"`
}

type errorJSON struct {
	Error            string `json:"error"`
	ErrorDescription string `json:"error_description"`
}

// decodeResponseToError will try do decode into problemJSON then errorJSON
// and extract details from this defined formats.
// It will fallback to creating and error with message body
// Second parameter is an error message
func decodeResponseToError(buffer []byte, msg string) error {
	problem := problemJSON{}
	err := json.Unmarshal(buffer, &problem)
	if err == nil {
		return errors.Errorf("%s: %s", msg, problem.Detail)
	}
	errJSON := &errorJSON{}

	err = json.Unmarshal(buffer, &errJSON)
	if err == nil {
		return errors.Errorf("%s: %s", msg, errJSON.ErrorDescription)
	}
	return errors.Errorf("%s: %s", msg, string(buffer))
}

// backOffConfiguration holds initial values for the initialization of a backoff that can
// be used in retries.
type backOffConfiguration struct {
	// Whether to retry failed request or not.
	Retry bool
	// The initial (minimal) retry interval used for the exponential backoff.
	InitialRetryInterval time.Duration
	// MaxRetryInterval the maximum retry interval.
	MaxRetryInterval time.Duration
	// MaxElapsedTime is the maximum time spent on retries.
	MaxElapsedTime time.Duration
}

// create initializes a new backoff from configured parameters.
func (rc *backOffConfiguration) create() backoff.BackOff {
	if !rc.Retry {
		return &backoff.StopBackOff{}
	}

	back := backoff.NewExponentialBackOff()
	back.InitialInterval = rc.InitialRetryInterval
	back.MaxInterval = rc.MaxRetryInterval
	back.MaxElapsedTime = rc.MaxElapsedTime
	back.Reset()

	return back
}
<commit_msg>added KeepAlive tcp tiemout for stream http transport<commit_after>package nakadi

import (
	"encoding/json"
	"net"
	"net/http"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/pkg/errors"
)

const (
	// defaults used by http.DefaultTransport
	defaultKeepAlive       = 30 * time.Second
	defaultIdleConnTimeout = 90 * time.Second
	// nakadi specific timeouts
	nakadiHeartbeatInterval = 30 * time.Second
)

// newHTTPClient crates an http client which is used for non streaming requests.
func newHTTPClient(timeout time.Duration) *http.Client {
	return &http.Client{
		Timeout: timeout,
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   timeout,
				KeepAlive: defaultKeepAlive,
				DualStack: true,
			}).DialContext,
			MaxIdleConns:        100,
			IdleConnTimeout:     defaultIdleConnTimeout,
			TLSHandshakeTimeout: timeout,
		},
	}
}

// newHTTPStream creates an http client which is used for streaming purposes.
func newHTTPStream(timeout time.Duration) *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   timeout,
				KeepAlive: 2 * nakadiHeartbeatInterval,
				DualStack: true,
			}).DialContext,
			MaxIdleConns:        100,
			IdleConnTimeout:     2 * nakadiHeartbeatInterval,
			TLSHandshakeTimeout: timeout,
		},
	}
}

// problemJSON is used to decode error responses.
type problemJSON struct {
	Title  string `json:"title"`
	Detail string `json:"detail"`
	Status int    `json:"status"`
	Type   string `json:"type"`
}

type errorJSON struct {
	Error            string `json:"error"`
	ErrorDescription string `json:"error_description"`
}

// decodeResponseToError will try do decode into problemJSON then errorJSON
// and extract details from this defined formats.
// It will fallback to creating and error with message body
// Second parameter is an error message
func decodeResponseToError(buffer []byte, msg string) error {
	problem := problemJSON{}
	err := json.Unmarshal(buffer, &problem)
	if err == nil {
		return errors.Errorf("%s: %s", msg, problem.Detail)
	}
	errJSON := &errorJSON{}

	err = json.Unmarshal(buffer, &errJSON)
	if err == nil {
		return errors.Errorf("%s: %s", msg, errJSON.ErrorDescription)
	}
	return errors.Errorf("%s: %s", msg, string(buffer))
}

// backOffConfiguration holds initial values for the initialization of a backoff that can
// be used in retries.
type backOffConfiguration struct {
	// Whether to retry failed request or not.
	Retry bool
	// The initial (minimal) retry interval used for the exponential backoff.
	InitialRetryInterval time.Duration
	// MaxRetryInterval the maximum retry interval.
	MaxRetryInterval time.Duration
	// MaxElapsedTime is the maximum time spent on retries.
	MaxElapsedTime time.Duration
}

// create initializes a new backoff from configured parameters.
func (rc *backOffConfiguration) create() backoff.BackOff {
	if !rc.Retry {
		return &backoff.StopBackOff{}
	}

	back := backoff.NewExponentialBackOff()
	back.InitialInterval = rc.InitialRetryInterval
	back.MaxInterval = rc.MaxRetryInterval
	back.MaxElapsedTime = rc.MaxElapsedTime
	back.Reset()

	return back
}
<|endoftext|>


<commit_before>package mcstore

import (
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/config"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db"
	"gopkg.in/olivere/elastic.v2"
)

// NewServicesContainer creates a new restful.Container made up of all
// the rest resources handled by the server.
func NewServicesContainer(sc db.SessionCreater) *restful.Container {
	container := restful.NewContainer()

	databaseSessionFilter := &databaseSessionFilter{
		session: sc.RSession,
	}
	container.Filter(databaseSessionFilter.Filter)

	apikeyFilter := newAPIKeyFilter(apiKeyCache)
	container.Filter(apikeyFilter.Filter)

	if config.GetBool("MCSTORED_MONITOR_USERS") {
		// launch routine to track changes to users and
		// update the keycache appropriately.
		go updateKeyCacheOnChange(sc.RSessionMust(), apiKeyCache)
	}

	if config.GetBool("MCSTORED_MONITOR_DB_CHANGES") {
		// launch routines to monitor for database changes
		launchSearchIndexChangeMonitors(sc)
	}

	uploadResource := newUploadResource()
	container.Add(uploadResource.WebService())

	projectsResource := newProjectsResource()
	container.Add(projectsResource.WebService())

	searchResource := newSearchResource()
	container.Add(searchResource.WebService())

	return container
}

func launchSearchIndexChangeMonitors(sc db.SessionCreater) {
	esclient := esClientMust()
	session := sc.RSessionMust()

	go processChangeIndexer(esclient, session)
	go fileChangeIndexer(esclient, session)
	go sampleChangeIndexer(esclient, session)
	go noteChangeIndexer(esclient, session)
	go propertysetChangeIndexer(esclient, session)
	go sampleDatafileChangeIndexer(esclient, session)
	go tagChangeIndexer(esclient, session)
	go projectFileChangeIndexer(esclient, session)
	go noteItemChangeIndexer(esclient, session)
}

func esClientMust() *elastic.Client {
	url := esURL()
	app.Log.Infof("Connecting to search url: %s", url)
	c, err := elastic.NewClient(elastic.SetURL(url))
	if err != nil {
		app.Log.Panicf("Couldn't connect to ElasticSearch")
	}
	return c
}

func esURL() string {
	if esURL := config.GetString("MC_ES_URL"); esURL != "" {
		return esURL
	}
	return "http://localhost:9200"
}
<commit_msg>Don't panic if you cannot connect to elasticsearch.<commit_after>package mcstore

import (
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/config"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db"
	"gopkg.in/olivere/elastic.v2"
)

// NewServicesContainer creates a new restful.Container made up of all
// the rest resources handled by the server.
func NewServicesContainer(sc db.SessionCreater) *restful.Container {
	container := restful.NewContainer()

	databaseSessionFilter := &databaseSessionFilter{
		session: sc.RSession,
	}
	container.Filter(databaseSessionFilter.Filter)

	apikeyFilter := newAPIKeyFilter(apiKeyCache)
	container.Filter(apikeyFilter.Filter)

	if config.GetBool("MCSTORED_MONITOR_USERS") {
		// launch routine to track changes to users and
		// update the keycache appropriately.
		go updateKeyCacheOnChange(sc.RSessionMust(), apiKeyCache)
	}

	if config.GetBool("MCSTORED_MONITOR_DB_CHANGES") {
		// launch routines to monitor for database changes
		launchSearchIndexChangeMonitors(sc)
	}

	uploadResource := newUploadResource()
	container.Add(uploadResource.WebService())

	projectsResource := newProjectsResource()
	container.Add(projectsResource.WebService())

	searchResource := newSearchResource()
	container.Add(searchResource.WebService())

	return container
}

func launchSearchIndexChangeMonitors(sc db.SessionCreater) {
	esclient := esClientMust()
	session := sc.RSessionMust()

	go processChangeIndexer(esclient, session)
	go fileChangeIndexer(esclient, session)
	go sampleChangeIndexer(esclient, session)
	go noteChangeIndexer(esclient, session)
	go propertysetChangeIndexer(esclient, session)
	go sampleDatafileChangeIndexer(esclient, session)
	go tagChangeIndexer(esclient, session)
	go projectFileChangeIndexer(esclient, session)
	go noteItemChangeIndexer(esclient, session)
}

func esClientMust() *elastic.Client {
	url := esURL()
	app.Log.Infof("Connecting to search url: %s", url)
	c, err := elastic.NewClient(elastic.SetURL(url))
	if err != nil {
		app.Log.Errorf("Couldn't connect to ElasticSearch")
	}
	return c
}

func esURL() string {
	if esURL := config.GetString("MC_ES_URL"); esURL != "" {
		return esURL
	}
	return "http://localhost:9200"
}
<|endoftext|>