<commit_before>/*
Kwiscale command line interface.
*/
package main

import (
	"bytes"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"text/template"

	"github.com/codegangsta/cli"
	"gopkg.in/yaml.v2"
)

var GOPATH = os.Getenv("GOPATH")

const (
	PROJECT_OPT     = "project"      // argument to set project name
	HANDLER_OPT     = "handlers"     // argument to set handlers name
	PROJECT_DEFAULT = "kwiscale-app" // default project name
	HANDLER_DEFAULT = "handlers"     // default handlers package name
)

type ymlstruct map[string]interface{}

func main() {

	out := cli.StringFlag{
		Name:   PROJECT_OPT,
		Value:  PROJECT_DEFAULT,
		Usage:  "project name, will set " + GOPATH + "/src/[projectname]",
		EnvVar: "KWISCALE_PROJECT",
	}

	handlers := cli.StringFlag{
		Name:   HANDLER_OPT,
		Value:  HANDLER_DEFAULT,
		Usage:  "handlers package name",
		EnvVar: "KWISCALE_HANDLERS",
	}

	app := cli.NewApp()
	app.Flags = []cli.Flag{out, handlers}
	app.Name = "kwiscale"
	app.Usage = "tool to manage kwiscale application"
	app.Version = "0.0.1"
	app.EnableBashCompletion = true
	app.Commands = []cli.Command{
		{
			Name:  "new",
			Usage: "Generate resources (application, handlers...)",
			Subcommands: []cli.Command{
				{
					Name:   "app",
					Usage:  "Create application",
					Action: newApplication,
				},
				{
					Name:   "handler",
					Usage:  "Create handler",
					Action: newHandler,
				},
			},
		},
		{
			Name:   "generate",
			Usage:  "Parse configuration and generate handlers, main file...",
			Action: parseConfig,
		},
	}

	app.Run(os.Args)
}

// create an application
func newApplication(c *cli.Context) {
	out := getProjectPath(c)
	log.Println("Create application in directory:", out)

	createDirectories(out, c.GlobalString(HANDLER_OPT))
	createConfig(c)
	createApp(c)
}

// add handler in config.yml
func newHandler(c *cli.Context) {
	var (
		y           = loadYaml(c)
		hpkg        = c.GlobalString(HANDLER_OPT)
		name        = c.Args().First()
		realname    = c.Args().Get(2)
		handlername = hpkg + "." + strings.Title(name) + "Handler"
		route       = c.Args().Get(1)
		m           = map[string]string{}
	)

	m["handler"] = handlername
	if realname != "" {
		m["alias"] = realname
	}

	if _, ok := y["routes"]; !ok {
		y["routes"] = make(map[interface{}]interface{})
	}

	// append handler
	routes := y["routes"].(map[interface{}]interface{})

	routes[route] = m

	b, _ := yaml.Marshal(y)
	cfg := filepath.Join(getProjectPath(c), "config.yml")
	ioutil.WriteFile(cfg, b, 0666)

	// now, parse config to generate/change handlers
	// and main.go file
	//parseConfig(c)

	createHandlerFile(handlername, c.GlobalString(HANDLER_OPT), getProjectPath(c))

}

// parse config file to generate handler file in handlers package
// by calling createHandlerFile, then
// call addHandlerInApp to append handlers in main.go
func parseConfig(c *cli.Context) {
	y := loadYaml(c)

	if _, ok := y["routes"]; !ok {
		log.Fatal("There are no route to create in configuration file")
	}

	routes := y["routes"]
	handlers := make([]map[string]string, 0)
	for route, v := range routes.(map[interface{}]interface{}) {
		handlername := v.(map[interface{}]interface{})["handler"].(string)
		alias := ""
		if a, ok := v.(map[interface{}]interface{})["alias"]; ok {
			alias = a.(string)
		}
		createHandlerFile(handlername, c.GlobalString(HANDLER_OPT), getProjectPath(c))
		handlers = append(handlers, map[string]string{
			"handler": handlername,
			"route":   route.(string),
			"alias":   alias,
		})
	}
	//addHandlersInApp(handlers, c)

}

// create handler file
func createHandlerFile(handler, handlerpkg, where string) {
	var (
		parts    = strings.Split(handler, ".")
		name     = parts[1]
		filename = strings.ToLower(strings.Replace(name, "Handler", "", -1))
		path     = filepath.Join(where, handlerpkg, filename+".go")
	)

	if _, err := os.Stat(path); err == nil {
		log.Println("Handler file already exists:", path)
		return
	}

	tpl, _ := template.New("handler").Parse(TPLHANDLER)

	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatal(err)
	}

	tpl.Execute(f, map[string]string{
		"Handler":     name,
		"HandlersPKG": handlerpkg,
	})
	log.Println("Handler created:", path)
}

// add handlers in main.go
func addHandlersInApp(handlers []map[string]string, c *cli.Context) {
	var (
		where   = getProjectPath(c)
		path    = filepath.Join(where, "main.go")
		content = TPLAPP
	)

	if _, err := os.Stat(path); err == nil {
		// reuse main.go
		c, _ := ioutil.ReadFile(path)
		content = string(c)
	} else {
		// build main.go content
		hpkg := c.GlobalString(HANDLER_OPT)
		var b []byte
		buffer := bytes.NewBuffer(b)
		tpl, _ := template.New("handler").Parse(content)
		tpl.Execute(buffer, map[string]string{
			"HandlersPKG": hpkg,
		})
		content = buffer.String()
	}

	tpladdnamedroute, _ := template.New("route").Parse(TPLADDNAMEDROUTE)

	lines := strings.Split(content, "\n")
	re := regexp.MustCompile(`@handlers@`)
	endre := regexp.MustCompile(`@end handlers@`)
	detected := false

	newcontent := make([]string, 0)
	for _, l := range lines {
		if endre.MatchString(l) {
			detected = false
		}
		if !detected {
			newcontent = append(newcontent, l)
		}
		if re.MatchString(l) {
			detected = true
			for _, route := range handlers {
				var b []byte
				buffer := bytes.NewBuffer(b)

				// select good template
				tpladdnamedroute.Execute(buffer, map[string]interface{}{
					"Route": route,
				})
				newcontent = append(newcontent, buffer.String())
			}
		}
	}

	m := strings.Join(newcontent, "\n")
	ioutil.WriteFile(path, []byte(m), 0666)

	// launch goimports and go fmt
	cmd := exec.Command("goimports", "-w", path)
	if err := cmd.Run(); err != nil {
		log.Println("[WARN] goimports error", err)
	}

	cmd = exec.Command("go", "fmt", path)
	if err := cmd.Run(); err != nil {
		log.Println("[WARN] go fmt error", err)
	}
}

// create directory tree
func createDirectories(out, hpkg string) {
	for _, p := range []string{
		hpkg,
		"templates",
		"statics",
	} {

		path := filepath.Join(out, p)
		if err := os.MkdirAll(path, 0776); err != nil {
			log.Println(err)
		}
	}
}

// create a config.yml file
func createConfig(c *cli.Context) {
	out := getProjectPath(c)
	appname := c.GlobalString(PROJECT_OPT)
	p := filepath.Join(out, "config.yml")

	y := ymlstruct{
		"listen":    ":8000",
		"staticdir": "./statics",
		"session": map[string]string{
			"secret": "Change this to a secret passphrase",
			"name":   appname,
		},
		"template": map[string]string{
			"dir": "./templates",
		},
	}
	b, _ := yaml.Marshal(y)

	ioutil.WriteFile(p, b, 0666)
}

// create application in project
func createApp(c *cli.Context) {
	out := getProjectPath(c)
	p := filepath.Join(out, "main.go")

	f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Print(err)
		return
	}
	defer f.Close()

	tpl, _ := template.New("main").Parse(TPLAPP)
	tpl.Execute(f, map[string]string{
		"Project":     c.GlobalString(PROJECT_OPT),
		"HandlersPKG": c.GlobalString(HANDLER_OPT),
	})

}

// returns project path
func getProjectPath(c *cli.Context) string {
	to := c.GlobalString(PROJECT_OPT)
	return filepath.Join(GOPATH, "src", to)
}

// load config.yml file
func loadYaml(c *cli.Context) ymlstruct {
	out := getProjectPath(c)
	out = filepath.Join(out, "config.yml")
	b, err := ioutil.ReadFile(out)
	if err != nil {
		log.Fatal(err)
	}

	y := ymlstruct{}
	yaml.Unmarshal(b, y)
	return y
}
<commit_msg>Cleanup and remove unused functions<commit_after>/*
Kwiscale command line interface.
*/
package main

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/codegangsta/cli"
	"gopkg.in/yaml.v2"
)

var GOPATH = os.Getenv("GOPATH")

const (
	PROJECT_OPT     = "project"      // argument to set project name
	HANDLER_OPT     = "handlers"     // argument to set handlers name
	PROJECT_DEFAULT = "kwiscale-app" // default project name
	HANDLER_DEFAULT = "handlers"     // default handlers package name
)

type ymlstruct map[string]interface{}

func main() {

	out := cli.StringFlag{
		Name:   PROJECT_OPT,
		Value:  PROJECT_DEFAULT,
		Usage:  "project name, will set " + GOPATH + "/src/[projectname]",
		EnvVar: "KWISCALE_PROJECT",
	}

	handlers := cli.StringFlag{
		Name:   HANDLER_OPT,
		Value:  HANDLER_DEFAULT,
		Usage:  "handlers package name",
		EnvVar: "KWISCALE_HANDLERS",
	}

	app := cli.NewApp()
	app.Flags = []cli.Flag{out, handlers}
	app.Name = "kwiscale"
	app.Usage = "tool to manage kwiscale application"
	app.Version = "0.0.1"
	app.EnableBashCompletion = true
	app.Commands = []cli.Command{
		{
			Name:  "new",
			Usage: "Generate resources (application, handlers...)",
			Subcommands: []cli.Command{
				{
					Name:   "app",
					Usage:  "Create application",
					Action: newApplication,
				},
				{
					Name:   "handler",
					Usage:  "Create handler",
					Action: newHandler,
				},
			},
		},
		{
			Name:   "generate",
			Usage:  "Parse configuration and generate handlers, main file...",
			Action: parseConfig,
		},
	}

	app.Run(os.Args)
}

// create an application
func newApplication(c *cli.Context) {
	out := getProjectPath(c)
	log.Println("Create application in directory:", out)

	createDirectories(out, c.GlobalString(HANDLER_OPT))
	createConfig(c)
	createApp(c)
}

// add handler in config.yml
func newHandler(c *cli.Context) {
	var (
		y           = loadYaml(c)
		hpkg        = c.GlobalString(HANDLER_OPT)
		name        = c.Args().First() //handler short name
		realname    = c.Args().Get(2)  //alias if any
		handlername = hpkg + "." + strings.Title(name) + "Handler"
		route       = c.Args().Get(1)
		m           = map[string]string{} //a simple map to handle route for template
	)

	// create handler file
	createHandlerFile(handlername, c.GlobalString(HANDLER_OPT), getProjectPath(c))

	// change configuration file
	m["handler"] = handlername
	if realname != "" {
		m["alias"] = realname
	}

	if _, ok := y["routes"]; !ok {
		y["routes"] = make(map[interface{}]interface{})
	}

	// append handler
	routes := y["routes"].(map[interface{}]interface{})

	routes[route] = m

	b, _ := yaml.Marshal(y)
	cfg := filepath.Join(getProjectPath(c), "config.yml")
	ioutil.WriteFile(cfg, b, 0666)
}

// parse config file to generate handler file in handlers package
// by calling createHandlerFile, then
// call addHandlerInApp to append handlers in main.go
func parseConfig(c *cli.Context) {
	y := loadYaml(c)

	if _, ok := y["routes"]; !ok {
		log.Fatal("There are no route to create in configuration file")
	}

	routes := y["routes"]
	handlers := make([]map[string]string, 0)
	for route, v := range routes.(map[interface{}]interface{}) {
		handlername := v.(map[interface{}]interface{})["handler"].(string)
		alias := ""
		if a, ok := v.(map[interface{}]interface{})["alias"]; ok {
			alias = a.(string)
		}
		createHandlerFile(handlername, c.GlobalString(HANDLER_OPT), getProjectPath(c))
		handlers = append(handlers, map[string]string{
			"handler": handlername,
			"route":   route.(string),
			"alias":   alias,
		})
	}
}

// create handler file.
func createHandlerFile(handler, handlerpkg, where string) {
	var (
		parts    = strings.Split(handler, ".")
		name     = parts[1]
		filename = strings.ToLower(strings.Replace(name, "Handler", "", -1))
		path     = filepath.Join(where, handlerpkg, filename+".go")
	)

	if _, err := os.Stat(path); err == nil {
		log.Println("Handler file already exists:", path)
		return
	}

	tpl, _ := template.New("handler").Parse(TPLHANDLER)

	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatal(err)
	}

	tpl.Execute(f, map[string]string{
		"Handler":     name,
		"HandlersPKG": handlerpkg,
	})

	log.Println("Handler created:", path)
}

// create directory tree
func createDirectories(out, hpkg string) {
	for _, p := range []string{
		hpkg,
		"templates",
		"statics",
	} {

		path := filepath.Join(out, p)
		if err := os.MkdirAll(path, 0776); err != nil {
			log.Println(err)
		}
	}
}

// create a config.yml file
func createConfig(c *cli.Context) {
	out := getProjectPath(c)
	appname := c.GlobalString(PROJECT_OPT)
	p := filepath.Join(out, "config.yml")

	y := ymlstruct{
		"listen":    ":8000",
		"staticdir": "./statics",
		"session": map[string]string{
			"secret": "Change this to a secret passphrase",
			"name":   appname,
		},
		"template": map[string]string{
			"dir": "./templates",
		},
	}
	b, _ := yaml.Marshal(y)

	ioutil.WriteFile(p, b, 0666)
}

// create application in project
func createApp(c *cli.Context) {
	out := getProjectPath(c)
	p := filepath.Join(out, "main.go")

	f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Print(err)
		return
	}
	defer f.Close()

	tpl, _ := template.New("main").Parse(TPLAPP)
	tpl.Execute(f, map[string]string{
		"Project":     c.GlobalString(PROJECT_OPT),
		"HandlersPKG": c.GlobalString(HANDLER_OPT),
	})

}

// returns project path
func getProjectPath(c *cli.Context) string {
	to := c.GlobalString(PROJECT_OPT)
	return filepath.Join(GOPATH, "src", to)
}

// load config.yml file
func loadYaml(c *cli.Context) ymlstruct {
	out := getProjectPath(c)
	out = filepath.Join(out, "config.yml")
	b, err := ioutil.ReadFile(out)
	if err != nil {
		log.Fatal(err)
	}

	y := ymlstruct{}
	yaml.Unmarshal(b, y)
	return y
}
<|endoftext|>


<commit_before>package auth

import (
	"errors"
	"fmt"
	"net/http"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/memcache"
)

const (
	tokensTable = "tokens"
	cookieName  = "app-cookie"
)

// Token keys
const (
	newTokenHeader       string = "new-auth-token"
	newTokenExpiryHeader string = "new-auth-token-expiry"
)

// Middleware .
type Middleware struct {
	// Reference to the session that is used to set/get account data
	Session Session

	// Whether the request should be allowed to continue if the token has expired, or no token exists.
	// This is useful for pages or endpoints that render/return differently based on whethe the user
	// is authenticated or not
	ContinueWithBadToken bool
}

// AuthenticateCookie authenticates the token with a request cookie
func (m *Middleware) AuthenticateCookie(c context.Context, w http.ResponseWriter, r *http.Request) context.Context {
	var accountKey *datastore.Key

	c, cancel := context.WithCancel(c)
	returnURL := fmt.Sprintf("/signin?returnUrl=%s", r.RequestURI)

	run := func() error {
		cookie, err := r.Cookie(cookieName)
		if err != nil {
			return fmt.Errorf("failed getting cookie: %v", err)
		}

		token, err := m.getToken(c, cookie.Value)
		if err != nil {
			return fmt.Errorf("failed to get token: %v", err)
		}
		if token.isExpired() {
			return errors.New("expired token")
		}

		accountKey = token.Key.Parent()

		// if the token's expiry less than a week away, get new token and kill the current
		if token.willExpireIn(time.Hour * 24 * 7) {
			newToken, err := m.getNewToken(c, token)
			if err != nil {
				return fmt.Errorf("failed to get new token: %v", err)
			}

			http.SetCookie(w, &http.Cookie{
				Name:     cookieName,
				Expires:  time.Now().Add(time.Hour * 24 * 14),
				HttpOnly: true,
				Secure:   !appengine.IsDevAppServer(),
				Value:    newToken.Value(),
			})
		}

		// add accountKey to context
		c = m.Session.SetAccountKey(c, accountKey)

		return nil
	}

	if err := run(); err != nil && !m.ContinueWithBadToken {
		http.Redirect(w, r, returnURL, http.StatusTemporaryRedirect)
		cancel()
	}

	return c
}

// AuthenticateToken authenticates the Authorization request header token
func (m *Middleware) AuthenticateToken(c context.Context, w http.ResponseWriter, r *http.Request) context.Context {
	// let option requests through
	if r.Method == http.MethodOptions {
		return c
	}

	c, cancel := context.WithCancel(c)

	run := func() error {
		authHeader := r.Header.Get("Authorization")
		if len(authHeader) <= len("token=") {
			return errors.New("missing token header")
		}

		// prevent token caching with blank string value
		rawToken := authHeader[len("token="):]
		if len(rawToken) == 0 {
			return errors.New("missing token value")
		}

		token, err := m.getToken(c, rawToken)
		if err != nil {
			return fmt.Errorf("failed to get token details: %v", err)
		}

		// if token has expired return 401
		if token.isExpired() {
			return errors.New("expired token")
		}

		accountKey := token.Key.Parent()

		// if the token's expiry less than a week away, get new token
		if token.willExpireIn(time.Hour * 24 * 7) {
			newToken, err := m.getNewToken(c, token)
			if err != nil {
				return fmt.Errorf("failed to create new token: %v", err)
			}

			// send back the new token values
			w.Header().Add(newTokenHeader, newToken.Value())
			w.Header().Add(newTokenExpiryHeader, newToken.Expiry.Format(time.RFC3339))
		}

		// add accountKey to context
		c = m.Session.SetAccountKey(c, accountKey)

		return nil
	}

	if err := run(); err != nil && !m.ContinueWithBadToken {
		w.WriteHeader(http.StatusUnauthorized)
		cancel()
	}

	return c
}

// Gets the token for the rawToken value
func (m *Middleware) getToken(c context.Context, rawToken string) (*Token, error) {
	var err error
	var token Token
	_, err = memcache.Gob.Get(c, rawToken, &token)
	if err == nil {
		return &token, nil
	}
	if err != memcache.ErrCacheMiss {
		return nil, fmt.Errorf("failed to get token from memcache: %v", err)
	}

	// not in memcache
	tokenKey, err := datastore.DecodeKey(rawToken)
	if err != nil {
		return nil, fmt.Errorf("decoding token key: %v", err)
	}

	var store = NewTokenStore()
	token.Key, err = store.Get(c, tokenKey, &token)
	if err != nil {
		return nil, fmt.Errorf("failed to get token from store: %v", err)
	}

	// add the token to memcache
	memcache.Gob.Set(c, &memcache.Item{
		Key:        token.Value(),
		Object:     token,
		Expiration: -1 * time.Since(token.Expiry),
	})
	return &token, nil
}

// Creates a new token and links it to the account for the old token
func (m *Middleware) getNewToken(c context.Context, oldToken *Token) (*Token, error) {
	store := NewTokenStore()
	newToken, err := store.Create(c, oldToken.Key.Parent())
	if err != nil {
		return nil, err
	}

	store.Delete(c, oldToken.Key)
	return newToken, nil
}
<commit_msg>fix to prevent periodic memcache errors<commit_after>package auth

import (
	"errors"
	"fmt"
	"net/http"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/memcache"
)

const (
	tokensTable = "tokens"
	cookieName  = "app-cookie"
)

// Token keys
const (
	newTokenHeader       string = "new-auth-token"
	newTokenExpiryHeader string = "new-auth-token-expiry"
)

// Middleware .
type Middleware struct {
	// Reference to the session that is used to set/get account data
	Session Session

	// Whether the request should be allowed to continue if the token has expired, or no token exists.
	// This is useful for pages or endpoints that render/return differently based on whethe the user
	// is authenticated or not
	ContinueWithBadToken bool
}

// AuthenticateCookie authenticates the token with a request cookie
func (m *Middleware) AuthenticateCookie(c context.Context, w http.ResponseWriter, r *http.Request) context.Context {
	var accountKey *datastore.Key

	c, cancel := context.WithCancel(c)
	returnURL := fmt.Sprintf("/signin?returnUrl=%s", r.RequestURI)

	run := func() error {
		cookie, err := r.Cookie(cookieName)
		if err != nil {
			return fmt.Errorf("failed getting cookie: %v", err)
		}

		token, err := m.getToken(c, cookie.Value)
		if err != nil {
			return fmt.Errorf("failed to get token: %v", err)
		}
		if token.isExpired() {
			return errors.New("expired token")
		}

		accountKey = token.Key.Parent()

		// if the token's expiry less than a week away, get new token and kill the current
		if token.willExpireIn(time.Hour * 24 * 7) {
			newToken, err := m.getNewToken(c, token)
			if err != nil {
				return fmt.Errorf("failed to get new token: %v", err)
			}

			http.SetCookie(w, &http.Cookie{
				Name:     cookieName,
				Expires:  time.Now().Add(time.Hour * 24 * 14),
				HttpOnly: true,
				Secure:   !appengine.IsDevAppServer(),
				Value:    newToken.Value(),
			})
		}

		// add accountKey to context
		c = m.Session.SetAccountKey(c, accountKey)

		return nil
	}

	if err := run(); err != nil && !m.ContinueWithBadToken {
		http.Redirect(w, r, returnURL, http.StatusTemporaryRedirect)
		cancel()
	}

	return c
}

// AuthenticateToken authenticates the Authorization request header token
func (m *Middleware) AuthenticateToken(c context.Context, w http.ResponseWriter, r *http.Request) context.Context {
	// let option requests through
	if r.Method == http.MethodOptions {
		return c
	}

	c, cancel := context.WithCancel(c)

	run := func() error {
		authHeader := r.Header.Get("Authorization")
		if len(authHeader) <= len("token=") {
			return errors.New("missing token header")
		}

		// prevent token caching with blank string value
		rawToken := authHeader[len("token="):]
		if len(rawToken) == 0 {
			return errors.New("missing token value")
		}

		token, err := m.getToken(c, rawToken)
		if err != nil {
			return fmt.Errorf("failed to get token details: %v", err)
		}

		// if token has expired return 401
		if token.isExpired() {
			return errors.New("expired token")
		}

		accountKey := token.Key.Parent()

		// if the token's expiry less than a week away, get new token
		if token.willExpireIn(time.Hour * 24 * 7) {
			newToken, err := m.getNewToken(c, token)
			if err != nil {
				return fmt.Errorf("failed to create new token: %v", err)
			}

			// send back the new token values
			w.Header().Add(newTokenHeader, newToken.Value())
			w.Header().Add(newTokenExpiryHeader, newToken.Expiry.Format(time.RFC3339))
		}

		// add accountKey to context
		c = m.Session.SetAccountKey(c, accountKey)

		return nil
	}

	if err := run(); err != nil && !m.ContinueWithBadToken {
		w.WriteHeader(http.StatusUnauthorized)
		cancel()
	}

	return c
}

// Gets the token for the rawToken value
func (m *Middleware) getToken(c context.Context, rawToken string) (*Token, error) {
	var err error
	var token Token
	item, err := memcache.Gob.Get(c, rawToken, nil)
	if err == nil {
		switch item.Object.(type) {
		case *Token:
			return item.Object.(*Token), nil
		}
	}
	if err != memcache.ErrCacheMiss {
		return nil, fmt.Errorf("failed to get token from memcache: %v", err)
	}

	// not in memcache
	tokenKey, err := datastore.DecodeKey(rawToken)
	if err != nil {
		return nil, fmt.Errorf("decoding token key: %v", err)
	}

	var store = NewTokenStore()
	token.Key, err = store.Get(c, tokenKey, &token)
	if err != nil {
		return nil, fmt.Errorf("failed to get token from store: %v", err)
	}

	// add the token to memcache
	memcache.Gob.Set(c, &memcache.Item{
		Key:        token.Value(),
		Object:     token,
		Expiration: -1 * time.Since(token.Expiry),
	})
	return &token, nil
}

// Creates a new token and links it to the account for the old token
func (m *Middleware) getNewToken(c context.Context, oldToken *Token) (*Token, error) {
	store := NewTokenStore()
	newToken, err := store.Create(c, oldToken.Key.Parent())
	if err != nil {
		return nil, err
	}

	store.Delete(c, oldToken.Key)
	return newToken, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"syscall"
	"time"
	"unsafe"

	"github.com/tinycedar/lily/common"
)

func main() {
	processes := getAllProcessIds()
	// common.Info("[getAllProcessIds]: %v", processes)
	for _, v := range processes {
		if v > 0 {
			openProcess(v)
		}
	}
}

func getAllProcessIds() []uint32 {
	now := time.Now()
	defer func(now time.Time) {
		common.Info("time elapsed: %v", time.Since(now))
	}(now)
	procEnumProcesses := syscall.NewLazyDLL("Psapi.dll").NewProc("EnumProcesses")
	var processes = make([]uint32, 1024)
	var cbNeeded uint32
	procEnumProcesses.Call(uintptr(unsafe.Pointer(&processes[0])), uintptr(len(processes)), uintptr(unsafe.Pointer(&cbNeeded)))
	if cbNeeded <= 0 {
		common.Error("Calling EnumProcesses returned empty")
		return nil
	}
	return processes[:cbNeeded/4]
}

func openProcess(pid uint32) uint32 {
	procOpenProcess := syscall.NewLazyDLL("Kernel32.dll").NewProc("OpenProcess")
	var dwDesiredAccess uint32 = 0x0400 | 0x0010
	openPid, _, _ := procOpenProcess.Call(uintptr(unsafe.Pointer(&dwDesiredAccess)), 0, uintptr(pid))
	if openPid > 0 {
		procEnumProcessModules := syscall.NewLazyDLL("Psapi.dll").NewProc("EnumProcessModules")
		var cbNeeded uint32
		var modules = make([]unsafe.Pointer, 10)
		if success, _, _ := procEnumProcessModules.Call(uintptr(openPid), uintptr(unsafe.Pointer(&modules[0])), 10, uintptr(unsafe.Pointer(&cbNeeded))); success > 0 {
			// fmt.Println("modules = ", modules)
			// var processName = "<unknown>------------------------"
			procGetModuleBaseName := syscall.NewLazyDLL("Psapi.dll").NewProc("GetModuleBaseNameA")
			var processName = make([]int8, 10240)
			// var processName = ""
			l, _, _ := procGetModuleBaseName.Call(uintptr(openPid), uintptr(unsafe.Pointer(modules[0])), uintptr(unsafe.Pointer(&processName)), uintptr(1024))
			if l > 0 {
				fmt.Println(processName[0:l])
			}
		}
	}
	return 0
}
<commit_msg>fix get process name bug<commit_after>package main

import (
	"fmt"
	"syscall"
	"time"
	"unsafe"

	"github.com/tinycedar/lily/common"
)

func main() {
	processes := getAllProcessIds()
	for _, v := range processes {
		if v > 0 {
			openProcess(v)
		}
	}
}

func getAllProcessIds() []uint32 {
	now := time.Now()
	defer func(now time.Time) {
		common.Info("time elapsed: %v", time.Since(now))
	}(now)
	procEnumProcesses := syscall.NewLazyDLL("Psapi.dll").NewProc("EnumProcesses")
	var processes = make([]uint32, 1024)
	var cbNeeded uint32
	procEnumProcesses.Call(uintptr(unsafe.Pointer(&processes[0])), uintptr(len(processes)), uintptr(unsafe.Pointer(&cbNeeded)))
	if cbNeeded <= 0 {
		common.Error("Calling EnumProcesses returned empty")
		return nil
	}
	return processes[:cbNeeded/4]
}

func openProcess(pid uint32) string {
	procOpenProcess := syscall.NewLazyDLL("Kernel32.dll").NewProc("OpenProcess")
	var dwDesiredAccess uint32 = 0x0400 | 0x0010
	openPid, _, errorStr := procOpenProcess.Call(uintptr(unsafe.Pointer(&dwDesiredAccess)), 0, uintptr(pid))
	if openPid > 0 {
		// procQueryFullProcessImageName := syscall.NewLazyDLL("Kernel32.dll").NewProc("QueryFullProcessImageNameA")
		// var processName = make([]byte, 1024)
		// var cbNeeded uint32 = 1024
		// procQueryFullProcessImageName.Call(uintptr(openPid), 0, uintptr(unsafe.Pointer(&processName[0])), uintptr(unsafe.Pointer(&cbNeeded)))
		// fmt.Println(string(processName[0:cbNeeded]))
		procEnumProcessModules := syscall.NewLazyDLL("Psapi.dll").NewProc("EnumProcessModules")
		var cbNeeded uint32
		var modules = make([]unsafe.Pointer, 10)
		if success, _, _ := procEnumProcessModules.Call(uintptr(openPid), uintptr(unsafe.Pointer(&modules[0])), 10, uintptr(unsafe.Pointer(&cbNeeded))); success > 0 {
			procGetModuleBaseName := syscall.NewLazyDLL("Psapi.dll").NewProc("GetModuleBaseNameA")
			var processName = make([]byte, 1024)
			var cbNeeded uint32 = 1024
			l, _, _ := procGetModuleBaseName.Call(uintptr(openPid), uintptr(unsafe.Pointer(modules[0])), uintptr(unsafe.Pointer(&processName[0])), uintptr(cbNeeded))
			if l > 0 {
				fmt.Println(string(processName[0:cbNeeded]))
			}
		}
	} else {
		fmt.Println(errorStr, pid)
	}
	return ""
}
<|endoftext|>


<commit_before>package meter2

import (
	"log"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	tc "github.com/alxarch/go-timecodec"
	"github.com/go-redis/redis"
)

const DefaultKeyPrefix = "meter:"

type DB struct {
	Redis     redis.UniversalClient
	Registry  *Registry
	KeyPrefix string
}

func NewDB(r redis.UniversalClient) *DB {
	db := new(DB)
	db.Redis = r
	db.Registry = defaultRegistry
	db.KeyPrefix = DefaultKeyPrefix
	return db
}

func (e *Desc) MatchingQueries(q url.Values) url.Values {
	if e == nil || q == nil {
		return nil
	}
	m := make(map[string][]string, len(q))
	for key, values := range q {
		if e.HasLabel(key) {
			m[key] = values
		}
	}
	return m
}

func TimeSequence(start time.Time, end time.Time, unit time.Duration) []time.Time {
	if unit == 0 {
		return []time.Time{}
	}
	start = tc.Round(start, unit).In(start.Location())
	end = tc.Round(end, unit).In(end.Location())
	n := end.Sub(start) / unit

	results := make([]time.Time, 0, n)

	for s := start; end.Sub(s) >= 0; s = s.Add(unit) {
		results = append(results, s)
	}
	return results
}

func QueryPermutations(input url.Values) []map[string]string {
	vcount := []int{}
	keys := []string{}
	combinations := [][]int{}
	for k, v := range input {
		if c := len(v); c > 0 {
			keys = append(keys, k)
			vcount = append(vcount, c)
		}
	}
	var generate func([]int)
	generate = func(comb []int) {
		if i := len(comb); i == len(vcount) {
			combinations = append(combinations, comb)
			return
		} else {
			for j := 0; j < vcount[i]; j++ {
				next := make([]int, i+1)
				if i > 0 {
					copy(next[:i], comb)
				}
				next[i] = j
				generate(next)
			}
		}
	}
	generate([]int{})
	results := make([]map[string]string, 0, len(combinations))
	for _, comb := range combinations {
		result := make(map[string]string, len(comb))
		for i, j := range comb {
			key := keys[i]
			result[key] = input[key][j]
		}
		if len(result) > 0 {
			results = append(results, result)
		}
	}
	return results
}

const LabelSeparator = '\x1f'
const FieldTerminator = '\x1e'

func (db DB) Key(r Resolution, event string, t time.Time) (k string) {
	return string(db.AppendKey(nil, r, event, t))
}

func (db DB) AppendKey(data []byte, r Resolution, event string, t time.Time) []byte {
	if db.KeyPrefix != "" {
		data = append(data, db.KeyPrefix...)
		data = append(data, LabelSeparator)
	}
	data = append(data, r.Name()...)
	data = append(data, LabelSeparator)
	data = append(data, r.MarshalTime(t)...)
	data = append(data, LabelSeparator)
	data = append(data, event...)
	return data
}

const NilByte byte = 0

func AppendField(data []byte, labels, values []string) []byte {
	n := len(values)
	for i := 0; i < len(labels); i++ {
		label := labels[i]
		if i > 0 {
			data = append(data, LabelSeparator)
		}
		data = append(data, label...)
		data = append(data, LabelSeparator)
		if i < n {
			value := values[i]
			data = append(data, value...)
		} else {
			data = append(data, NilByte)
		}
	}
	data = append(data, FieldTerminator)
	return data
}

// TODO: Write metrics in parallel with fan out

func (db *DB) Gather(col Collector) error {
	ch := make(chan Metric)
	result := make(chan error)
	go func() {
		pipeline := db.Redis.Pipeline()
		defer pipeline.Close()
		data := []byte{}
		tm := time.Now()
		pipelineSize := 0
		for m := range ch {
			if m == nil {
				continue
			}
			n := m.Set(0)
			if n == 0 {
				continue
			}
			values := m.Values()
			desc := m.Describe()
			name := desc.Name()
			labels := desc.Labels()
			data = AppendField(data[:0], labels, values)
			field := string(data)
			for _, res := range desc.Resolutions() {
				data = db.AppendKey(data[:0], res, name, tm)
				key := string(data)
				pipeline.HIncrBy(key, field, n)
				if ttl := res.TTL(); ttl > 0 {
					pipeline.Expire(key, ttl)
				}
				pipelineSize++
			}
		}
		if pipelineSize == 0 {
			result <- nil
			return
		}
		if _, err := pipeline.Exec(); err != nil && err != redis.Nil {
			log.Println(err)
			result <- err
			return
		}
		result <- nil
	}()
	col.Collect(ch)
	close(ch)
	return <-result
}

type ScanResult struct {
	Name   string
	Time   time.Time
	Group  string
	Values LabelValues
	err    error
	count  int64
}

func AppendMatch(data []byte, s string) []byte {
	for i := 0; i < len(s); i++ {
		switch b := s[i]; b {
		case '*', '[', ']', '?', '^':
			data = append(data, '\\', b)
		default:
			data = append(data, b)
		}
	}
	return data
}
func AppendMatchField(data []byte, labels []string, group string, q map[string]string) []byte {
	for i := 0; i < len(labels); i++ {
		if i > 0 {
			data = append(data, LabelSeparator)
		}
		label := labels[i]
		data = AppendMatch(data, label)
		data = append(data, LabelSeparator)
		if label == group {
			data = append(data, '[', '^', NilByte, ']', '*')
			continue
		}
		if q != nil {
			if v, ok := q[label]; ok {
				data = AppendMatch(data, v)
				continue
			}
		}
		data = append(data, '*')

	}
	data = append(data, FieldTerminator)
	return data
}

type ScanQuery struct {
	Event      string
	Start, End time.Time
	Group      string
	Query      url.Values
	Resolution Resolution
}

func (q ScanQuery) QueryValues(d *Desc) []map[string]string {
	if d == nil {
		return nil
	}
	if q.Group != "" {
		if !d.HasLabel(q.Group) {
			return nil
		}
		delete(q.Query, q.Group)
	}

	queries := d.MatchingQueries(q.Query)
	return QueryPermutations(queries)
}

func (db *DB) Query(q ScanQuery) (Results, error) {
	results := Results{}
	ch := make(chan ScanResult, 1)
	go func() {
		for r := range ch {
			results = r.AppendResults(results)
		}
	}()
	err := db.ScanQuery(q, ch)
	close(ch)
	return results, err
}

func (db *DB) ScanQuery(q ScanQuery, results chan<- ScanResult) (err error) {
	event := db.Registry.Get(q.Event)
	if event == nil {
		return ErrUnregisteredEvent
	}
	desc := event.Describe()
	if desc == nil {
		return ErrNilDesc
	}
	if q.Group != "" && !desc.HasLabel(q.Group) {
		return ErrInvalidEventLabel
	}
	queries := q.QueryValues(desc)
	if queries == nil {
		return nil
	}
	if len(queries) == 0 {
		queries = append(queries, map[string]string{})
	}
	ts := q.Resolution.TimeSequence(q.Start, q.End)
	if len(ts) == 0 {
		return nil
	}
	wg := &sync.WaitGroup{}
	result := ScanResult{
		Name: q.Event,
	}
	for _, values := range queries {
		result.Values = values
		m := event.WithLabels(values)
		if m == nil {
			continue
		}
		desc = m.Describe()
		if desc == nil {
			continue
		}

		if e := desc.Error(); e != nil {
			result.err = e
			results <- result
			continue
		}

		result.Name = desc.Name()
		data := AppendMatchField(nil, desc.Labels(), q.Group, values)
		match := string(data)
		// Let redis client pool size determine parallel requests
		for _, tm := range ts {
			result.Time = tm
			data = db.AppendKey(data[:0], q.Resolution, desc.Name(), tm)
			key := string(data)
			wg.Add(1)
			go func(r ScanResult, key string) {
				db.Scan(key, match, q.Group, r, results)
				wg.Done()
			}(result, key)
		}
	}
	wg.Wait()

	return
}

func scanField(val []byte, field, group string) []byte {
	i := strings.Index(field, group)
	if i == -1 {
		return val
	}
	i += len(group)
	for j := i; j < len(field); j++ {
		if field[j] == LabelSeparator || field[j] == FieldTerminator {
			return append(val, field[i:j]...)
		}
	}
	return val
}

const sLabelSeparator = "\x1f"

func (db *DB) Scan(key, match, group string, r ScanResult, results chan<- ScanResult) (err error) {
	var fields []string
	scan := db.Redis.HScan(key, 0, match, -1).Iterator()
	for scan.Next() {
		fields = append(fields, scan.Val())
	}
	if err = scan.Err(); err != nil {
		return
	}
	if len(fields) == 0 {
		return
	}
	var reply []interface{}
	reply, err = db.Redis.HMGet(key, fields...).Result()
	if err != nil {
		if err == redis.Nil {
			err = nil
		}
		return
	}
	var grouped map[string]int64
	var val []byte
	if group != "" {
		group = r.Group + sLabelSeparator
		val = []byte(group)
		grouped = make(map[string]int64, len(fields))
	}
	// Just in case
	r.count = 0
	for i, x := range reply {
		if a, ok := x.(string); ok {
			if n, e := strconv.ParseInt(a, 10, 64); e == nil {
				if group != "" {
					val = scanField(val[:0], fields[i], group)
					grouped[string(val)] += n
				} else {
					r.count += n
				}
			}
		}
	}
	if group != "" {
		for g, count := range grouped {
			r.Group = g
			r.count = count
			results <- r
		}
		return
	}
	results <- r

	return

}

func (r ScanResult) AppendResults(results Results) Results {
	p := DataPoint{r.Time.Unix(), r.count}
	if results != nil {
		if i := results.Find(r.Name, r.Group, r.Values); i != -1 {
			results[i].Data = append(results[i].Data, p)
			return results
		}
	}
	return append(results, Result{
		Event:  r.Name,
		Labels: r.Values,
		Group:  r.Group,
		Data:   DataPoints{p},
	})

}
<commit_msg>Have DB embed Registry for ease of use<commit_after>package meter2

import (
	"log"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	tc "github.com/alxarch/go-timecodec"
	"github.com/go-redis/redis"
)

const DefaultKeyPrefix = "meter:"

type DB struct {
	Redis redis.UniversalClient
	*Registry
	KeyPrefix string
}

func NewDB(r redis.UniversalClient) *DB {
	db := new(DB)
	db.Redis = r
	db.Registry = defaultRegistry
	db.KeyPrefix = DefaultKeyPrefix
	return db
}

func (e *Desc) MatchingQueries(q url.Values) url.Values {
	if e == nil || q == nil {
		return nil
	}
	m := make(map[string][]string, len(q))
	for key, values := range q {
		if e.HasLabel(key) {
			m[key] = values
		}
	}
	return m
}

func TimeSequence(start time.Time, end time.Time, unit time.Duration) []time.Time {
	if unit == 0 {
		return []time.Time{}
	}
	start = tc.Round(start, unit).In(start.Location())
	end = tc.Round(end, unit).In(end.Location())
	n := end.Sub(start) / unit

	results := make([]time.Time, 0, n)

	for s := start; end.Sub(s) >= 0; s = s.Add(unit) {
		results = append(results, s)
	}
	return results
}

func QueryPermutations(input url.Values) []map[string]string {
	vcount := []int{}
	keys := []string{}
	combinations := [][]int{}
	for k, v := range input {
		if c := len(v); c > 0 {
			keys = append(keys, k)
			vcount = append(vcount, c)
		}
	}
	var generate func([]int)
	generate = func(comb []int) {
		if i := len(comb); i == len(vcount) {
			combinations = append(combinations, comb)
			return
		} else {
			for j := 0; j < vcount[i]; j++ {
				next := make([]int, i+1)
				if i > 0 {
					copy(next[:i], comb)
				}
				next[i] = j
				generate(next)
			}
		}
	}
	generate([]int{})
	results := make([]map[string]string, 0, len(combinations))
	for _, comb := range combinations {
		result := make(map[string]string, len(comb))
		for i, j := range comb {
			key := keys[i]
			result[key] = input[key][j]
		}
		if len(result) > 0 {
			results = append(results, result)
		}
	}
	return results
}

const LabelSeparator = '\x1f'
const FieldTerminator = '\x1e'

func (db DB) Key(r Resolution, event string, t time.Time) (k string) {
	return string(db.AppendKey(nil, r, event, t))
}

func (db DB) AppendKey(data []byte, r Resolution, event string, t time.Time) []byte {
	if db.KeyPrefix != "" {
		data = append(data, db.KeyPrefix...)
		data = append(data, LabelSeparator)
	}
	data = append(data, r.Name()...)
	data = append(data, LabelSeparator)
	data = append(data, r.MarshalTime(t)...)
	data = append(data, LabelSeparator)
	data = append(data, event...)
	return data
}

const NilByte byte = 0

func AppendField(data []byte, labels, values []string) []byte {
	n := len(values)
	for i := 0; i < len(labels); i++ {
		label := labels[i]
		if i > 0 {
			data = append(data, LabelSeparator)
		}
		data = append(data, label...)
		data = append(data, LabelSeparator)
		if i < n {
			value := values[i]
			data = append(data, value...)
		} else {
			data = append(data, NilByte)
		}
	}
	data = append(data, FieldTerminator)
	return data
}

// TODO: Write metrics in parallel with fan out

func (db *DB) Sync() error {
	return db.Gather(db.Registry)
}

func (db *DB) Gather(col Collector) error {
	ch := make(chan Metric)
	result := make(chan error)
	go func() {
		pipeline := db.Redis.Pipeline()
		defer pipeline.Close()
		data := []byte{}
		tm := time.Now()
		pipelineSize := 0
		for m := range ch {
			if m == nil {
				continue
			}
			n := m.Set(0)
			if n == 0 {
				continue
			}
			values := m.Values()
			desc := m.Describe()
			name := desc.Name()
			labels := desc.Labels()
			data = AppendField(data[:0], labels, values)
			field := string(data)
			for _, res := range desc.Resolutions() {
				data = db.AppendKey(data[:0], res, name, tm)
				key := string(data)
				pipeline.HIncrBy(key, field, n)
				if ttl := res.TTL(); ttl > 0 {
					pipeline.Expire(key, ttl)
				}
				pipelineSize++
			}
		}
		if pipelineSize == 0 {
			result <- nil
			return
		}
		if _, err := pipeline.Exec(); err != nil && err != redis.Nil {
			log.Println(err)
			result <- err
			return
		}
		result <- nil
	}()
	col.Collect(ch)
	close(ch)
	return <-result
}

type ScanResult struct {
	Name   string
	Time   time.Time
	Group  string
	Values LabelValues
	err    error
	count  int64
}

func AppendMatch(data []byte, s string) []byte {
	for i := 0; i < len(s); i++ {
		switch b := s[i]; b {
		case '*', '[', ']', '?', '^':
			data = append(data, '\\', b)
		default:
			data = append(data, b)
		}
	}
	return data
}
func AppendMatchField(data []byte, labels []string, group string, q map[string]string) []byte {
	for i := 0; i < len(labels); i++ {
		if i > 0 {
			data = append(data, LabelSeparator)
		}
		label := labels[i]
		data = AppendMatch(data, label)
		data = append(data, LabelSeparator)
		if label == group {
			data = append(data, '[', '^', NilByte, ']', '*')
			continue
		}
		if q != nil {
			if v, ok := q[label]; ok {
				data = AppendMatch(data, v)
				continue
			}
		}
		data = append(data, '*')

	}
	data = append(data, FieldTerminator)
	return data
}

type ScanQuery struct {
	Event      string
	Start, End time.Time
	Group      string
	Query      url.Values
	Resolution Resolution
}

func (q ScanQuery) QueryValues(d *Desc) []map[string]string {
	if d == nil {
		return nil
	}
	if q.Group != "" {
		if !d.HasLabel(q.Group) {
			return nil
		}
		delete(q.Query, q.Group)
	}

	queries := d.MatchingQueries(q.Query)
	return QueryPermutations(queries)
}

func (db *DB) Query(q ScanQuery) (Results, error) {
	results := Results{}
	ch := make(chan ScanResult, 1)
	go func() {
		for r := range ch {
			results = r.AppendResults(results)
		}
	}()
	err := db.ScanQuery(q, ch)
	close(ch)
	return results, err
}

func (db *DB) ScanQuery(q ScanQuery, results chan<- ScanResult) (err error) {
	event := db.Registry.Get(q.Event)
	if event == nil {
		return ErrUnregisteredEvent
	}
	desc := event.Describe()
	if desc == nil {
		return ErrNilDesc
	}
	if q.Group != "" && !desc.HasLabel(q.Group) {
		return ErrInvalidEventLabel
	}
	queries := q.QueryValues(desc)
	if queries == nil {
		return nil
	}
	if len(queries) == 0 {
		queries = append(queries, map[string]string{})
	}
	ts := q.Resolution.TimeSequence(q.Start, q.End)
	if len(ts) == 0 {
		return nil
	}
	wg := &sync.WaitGroup{}
	result := ScanResult{
		Name: q.Event,
	}
	for _, values := range queries {
		result.Values = values
		m := event.WithLabels(values)
		if m == nil {
			continue
		}
		desc = m.Describe()
		if desc == nil {
			continue
		}

		if e := desc.Error(); e != nil {
			result.err = e
			results <- result
			continue
		}

		result.Name = desc.Name()
		data := AppendMatchField(nil, desc.Labels(), q.Group, values)
		match := string(data)
		// Let redis client pool size determine parallel requests
		for _, tm := range ts {
			result.Time = tm
			data = db.AppendKey(data[:0], q.Resolution, desc.Name(), tm)
			key := string(data)
			wg.Add(1)
			go func(r ScanResult, key string) {
				db.Scan(key, match, q.Group, r, results)
				wg.Done()
			}(result, key)
		}
	}
	wg.Wait()

	return
}

func scanField(val []byte, field, group string) []byte {
	i := strings.Index(field, group)
	if i == -1 {
		return val
	}
	i += len(group)
	for j := i; j < len(field); j++ {
		if field[j] == LabelSeparator || field[j] == FieldTerminator {
			return append(val, field[i:j]...)
		}
	}
	return val
}

const sLabelSeparator = "\x1f"

func (db *DB) Scan(key, match, group string, r ScanResult, results chan<- ScanResult) (err error) {
	var fields []string
	scan := db.Redis.HScan(key, 0, match, -1).Iterator()
	for scan.Next() {
		fields = append(fields, scan.Val())
	}
	if err = scan.Err(); err != nil {
		return
	}
	if len(fields) == 0 {
		return
	}
	var reply []interface{}
	reply, err = db.Redis.HMGet(key, fields...).Result()
	if err != nil {
		if err == redis.Nil {
			err = nil
		}
		return
	}
	var grouped map[string]int64
	var val []byte
	if group != "" {
		group = r.Group + sLabelSeparator
		val = []byte(group)
		grouped = make(map[string]int64, len(fields))
	}
	// Just in case
	r.count = 0
	for i, x := range reply {
		if a, ok := x.(string); ok {
			if n, e := strconv.ParseInt(a, 10, 64); e == nil {
				if group != "" {
					val = scanField(val[:0], fields[i], group)
					grouped[string(val)] += n
				} else {
					r.count += n
				}
			}
		}
	}
	if group != "" {
		for g, count := range grouped {
			r.Group = g
			r.count = count
			results <- r
		}
		return
	}
	results <- r

	return

}

func (r ScanResult) AppendResults(results Results) Results {
	p := DataPoint{r.Time.Unix(), r.count}
	if results != nil {
		if i := results.Find(r.Name, r.Group, r.Values); i != -1 {
			results[i].Data = append(results[i].Data, p)
			return results
		}
	}
	return append(results, Result{
		Event:  r.Name,
		Labels: r.Values,
		Group:  r.Group,
		Data:   DataPoints{p},
	})

}
<|endoftext|>


<commit_before>package influxdb_test

import (
	"testing"

	"github.com/influxdb/influxdb"
)

func TestIndex_MeasurementBySeriesID(t *testing.T) {
	idx := influxdb.NewIndex()
	m := &influxdb.Measurement{
		Name: "cpu_load",
		Series: []*influxdb.Series{
			&influxdb.Series{
				ID:   uint32(1),
				Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}}}

	// add it and see if we can look it up
	idx.AddSeries(m.Name, m.Series[0])
	mm := idx.MeasurementBySeriesID(uint32(1))
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	}

	// now test that we can add another
	s := &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
	m.Series = append(m.Series, s)

	idx.AddSeries(m.Name, s)
	mm = idx.MeasurementBySeriesID(uint32(2))
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	}

	mm = idx.MeasurementBySeriesID(uint32(1))
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	}
}

func TestIndex_MeasurementsBySeriesIDs(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_SeriesBySeriesID(t *testing.T) {
	idx := influxdb.NewIndex()

	// now test that we can add another
	s := &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}

	idx.AddSeries("foo", s)
	ss := idx.SeriesByID(uint32(2))
	if mustMarshalJSON(s) != mustMarshalJSON(ss) {
		t.Fatalf("series not equal:\n%s\n%s", s, ss)
	}
}

func TestIndex_MeasurementAndSeries(t *testing.T) {
	idx := influxdb.NewIndex()
	m := &influxdb.Measurement{
		Name: "cpu_load",
		Series: []*influxdb.Series{
			&influxdb.Series{
				ID:   uint32(1),
				Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}}}
	s := m.Series[0]

	// add it and see if we can look it up by name and tags
	idx.AddSeries(m.Name, s)
	mm, ss := idx.MeasurementAndSeries(m.Name, s.Tags)
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	} else if mustMarshalJSON(s) != mustMarshalJSON(ss) {
		t.Fatalf("series not equal:\n%s\n%s", s, ss)
	}

	// now test that we can add another
	s = &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
	m.Series = append(m.Series, s)

	idx.AddSeries(m.Name, s)
	mm, ss = idx.MeasurementAndSeries(m.Name, s.Tags)
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	} else if mustMarshalJSON(s) != mustMarshalJSON(ss) {
		t.Fatalf("series not equal:\n%s\n%s", s, ss)
	}
}

func TestIndex_SeriesIDs(t *testing.T) {
	idx := influxdb.NewIndex()
	s := &influxdb.Series{
		ID:   uint32(1),
		Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}

	// add it and see if we can look it up
	added := idx.AddSeries("cpu_load", s)
	if !added {
		t.Fatal("couldn't add series")
	}

	// test that we can't add it again
	added = idx.AddSeries("cpu_load", s)
	if added {
		t.Fatal("shoulnd't be able to add duplicate series")
	}

	// now test that we can add another
	s = &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
	added = idx.AddSeries("cpu_load", s)
	if !added {
		t.Fatalf("couldn't add series")
	}

	l := idx.SeriesIDs([]string{"cpu_load"}, nil)
	r := []uint32{1, 2}
	if !l.Equals(r) {
		t.Fatalf("series IDs not the same:\n%s\n%s", l, r)
	}

	// now add another in a different measurement
	s = &influxdb.Series{
		ID:   uint32(3),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
	added = idx.AddSeries("network_in", s)
	if !added {
		t.Fatalf("couldn't add series")
	}

	l = idx.SeriesIDs([]string{"cpu_load"}, nil)
	r = []uint32{1, 2, 3}
	if !l.Equals(r) {
		t.Fatalf("series IDs not the same:\n%s\n%s", l, r)
	}
}

func TestIndex_SeriesIDsWhereFilter(t *testing.T) {
	idx := indexWithFixtureData()

	var tests = []struct {
		names   []string
		filters []*influxdb.Filter
		result  []uint32
	}{
		// match against no tags
		{
			names:  []string{"cpu_load", "redis"},
			result: []uint32{uint32(1), uint32(2), uint32(3), uint32(4), uint32(5)},
		},

		// match against all tags
		{
			names: []string{"cpu_load"},
			filters: []*influxdb.Filter{
				&influxdb.Filter{Key: "host", Value: "servera.influx.com"},
				&influxdb.Filter{Key: "region", Value: "uswest"},
			},
			result: []uint32{uint32(1)},
		},

		// match against one tag
		{
			names: []string{"cpu_load"},
			filters: []*influxdb.Filter{
				&influxdb.Filter{Key: "region", Value: "uswest"},
			},
			result: []uint32{uint32(1), uint32(2)},
		},

		// partial match against one tag

		// partial match against two tags

		// query against tag key that doesn't exist returns empty

		// query against tag value that doesn't exist returns empty
	}

	for i, tt := range tests {
		r := idx.SeriesIDs(tt.names, tt.filters)
		expectedIDs := influxdb.SeriesIDs(tt.result)
		if !r.Equals(expectedIDs) {
			t.Fatalf("%d: filters: %s: result mismatch:\n  exp=%s\n  got=%s", i, influxdb.Filters(tt.filters), expectedIDs, r)
		}
	}
}

func TestIndex_SeriesIDsWhereFilterMultiple(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_SeriesIDsWhereNot(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_SeriesIDsWhereFilterAndNot(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_FieldKeys(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagKeys(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagKeysForMeasurement(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagValuesWhereFilter(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagValuesWhereFilterMultiple(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagValuesWhereNot(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagValuesWhereFilterAndNot(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_MeasurementsWhereFilter(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_DropSeries(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_DropMeasurement(t *testing.T) {
	t.Skip("pending")
}

// indexWithFixtureData returns a populated Index for use in many of the filtering tests
func indexWithFixtureData() *influxdb.Index {
	idx := influxdb.NewIndex()
	s := &influxdb.Series{
		ID:   uint32(1),
		Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}

	added := idx.AddSeries("cpu_load", s)
	if !added {
		return nil
	}

	s = &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}

	added = idx.AddSeries("cpu_load", s)
	if !added {
		return nil
	}

	s = &influxdb.Series{
		ID:   uint32(3),
		Tags: map[string]string{"host": "serverc.influx.com", "region": "uswest", "service": "redis"}}

	added = idx.AddSeries("key_count", s)
	if !added {
		return nil
	}

	s = &influxdb.Series{
		ID:   uint32(4),
		Tags: map[string]string{"host": "serverd.influx.com", "region": "useast", "service": "redis"}}

	added = idx.AddSeries("key_count", s)
	if !added {
		return nil
	}

	s = &influxdb.Series{
		ID:   uint32(5),
		Tags: map[string]string{"name": "high priority"}}

	added = idx.AddSeries("queue_depth", s)
	if !added {
		return nil
	}

	return idx
}
<commit_msg>Add more test cases for filtering and intersection.<commit_after>package influxdb_test

import (
	"testing"

	"github.com/influxdb/influxdb"
)

// Ensure that we can get the measurement by the series ID.
func TestIndex_MeasurementBySeriesID(t *testing.T) {
	idx := influxdb.NewIndex()
	m := &influxdb.Measurement{
		Name: "cpu_load",
		Series: []*influxdb.Series{
			&influxdb.Series{
				ID:   uint32(1),
				Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}}}

	// add it and see if we can look it up
	idx.AddSeries(m.Name, m.Series[0])
	mm := idx.MeasurementBySeriesID(uint32(1))
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	}

	// now test that we can add another
	s := &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
	m.Series = append(m.Series, s)

	idx.AddSeries(m.Name, s)
	mm = idx.MeasurementBySeriesID(uint32(2))
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	}

	mm = idx.MeasurementBySeriesID(uint32(1))
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	}
}

// Ensure that we can get an array of unique measurements by a collection of series IDs.
func TestIndex_MeasurementsBySeriesIDs(t *testing.T) {
	t.Skip("pending")
}

// Ensure that we can get the series object by the series ID.
func TestIndex_SeriesBySeriesID(t *testing.T) {
	idx := influxdb.NewIndex()

	// now test that we can add another
	s := &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}

	idx.AddSeries("foo", s)
	ss := idx.SeriesByID(uint32(2))
	if mustMarshalJSON(s) != mustMarshalJSON(ss) {
		t.Fatalf("series not equal:\n%s\n%s", s, ss)
	}
}

// Ensure that we can get the measurement and series objects out based on measurement and tags.
func TestIndex_MeasurementAndSeries(t *testing.T) {
	idx := influxdb.NewIndex()
	m := &influxdb.Measurement{
		Name: "cpu_load",
		Series: []*influxdb.Series{
			&influxdb.Series{
				ID:   uint32(1),
				Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}}}
	s := m.Series[0]

	// add it and see if we can look it up by name and tags
	idx.AddSeries(m.Name, s)
	mm, ss := idx.MeasurementAndSeries(m.Name, s.Tags)
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	} else if mustMarshalJSON(s) != mustMarshalJSON(ss) {
		t.Fatalf("series not equal:\n%s\n%s", s, ss)
	}

	// now test that we can add another
	s = &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
	m.Series = append(m.Series, s)

	idx.AddSeries(m.Name, s)
	mm, ss = idx.MeasurementAndSeries(m.Name, s.Tags)
	if mustMarshalJSON(m) != mustMarshalJSON(mm) {
		t.Fatalf("mesurement not equal:\n%s\n%s", m, mm)
	} else if mustMarshalJSON(s) != mustMarshalJSON(ss) {
		t.Fatalf("series not equal:\n%s\n%s", s, ss)
	}
}

// Ensure that we can get the series IDs for measurements without any filters.
func TestIndex_SeriesIDs(t *testing.T) {
	idx := influxdb.NewIndex()
	s := &influxdb.Series{
		ID:   uint32(1),
		Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}

	// add it and see if we can look it up
	added := idx.AddSeries("cpu_load", s)
	if !added {
		t.Fatal("couldn't add series")
	}

	// test that we can't add it again
	added = idx.AddSeries("cpu_load", s)
	if added {
		t.Fatal("shoulnd't be able to add duplicate series")
	}

	// now test that we can add another
	s = &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
	added = idx.AddSeries("cpu_load", s)
	if !added {
		t.Fatalf("couldn't add series")
	}

	l := idx.SeriesIDs([]string{"cpu_load"}, nil)
	r := []uint32{1, 2}
	if !l.Equals(r) {
		t.Fatalf("series IDs not the same:\n%s\n%s", l, r)
	}

	// now add another in a different measurement
	s = &influxdb.Series{
		ID:   uint32(3),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}
	added = idx.AddSeries("network_in", s)
	if !added {
		t.Fatalf("couldn't add series")
	}

	l = idx.SeriesIDs([]string{"cpu_load"}, nil)
	r = []uint32{1, 2, 3}
	if !l.Equals(r) {
		t.Fatalf("series IDs not the same:\n%s\n%s", l, r)
	}
}

func TestIndex_SeriesIDsWhereFilter(t *testing.T) {
	idx := indexWithFixtureData()

	var tests = []struct {
		names   []string
		filters []*influxdb.Filter
		result  []uint32
	}{
		// match against no tags
		{
			names:  []string{"cpu_load", "redis"},
			result: []uint32{uint32(1), uint32(2), uint32(3), uint32(4), uint32(5)},
		},

		// match against all tags
		{
			names: []string{"cpu_load"},
			filters: []*influxdb.Filter{
				&influxdb.Filter{Key: "host", Value: "servera.influx.com"},
				&influxdb.Filter{Key: "region", Value: "uswest"},
			},
			result: []uint32{uint32(1)},
		},

		// match against one tag
		{
			names: []string{"cpu_load"},
			filters: []*influxdb.Filter{
				&influxdb.Filter{Key: "region", Value: "uswest"},
			},
			result: []uint32{uint32(1), uint32(2)},
		},

		// match against one tag, single result
		{
			names: []string{"cpu_load"},
			filters: []*influxdb.Filter{
				&influxdb.Filter{Key: "host", Value: "servera.influx.com"},
			},
			result: []uint32{uint32(1)},
		},

		// query against tag key that doesn't exist returns empty
		{
			names: []string{"cpu_load"},
			filters: []*influxdb.Filter{
				&influxdb.Filter{Key: "foo", Value: "bar"},
			},
			result: []uint32{},
		},

		// query against tag value that doesn't exist returns empty
		{
			names: []string{"cpu_load"},
			filters: []*influxdb.Filter{
				&influxdb.Filter{Key: "host", Value: "foo"},
			},
			result: []uint32{},
		},

		// query against a tag NOT value

		// query against a tag NOT null

		// query against a tag value and a NOT value on the same key

		// query against a tag value and another tag NOT value

		// query against a tag value matching regex

		// query against a tag value matching regex and other tag value matching value

		// query against a tag value NOT matching regex

		// query against a tag value NOT matching regex and other tag value matching value
	}

	for i, tt := range tests {
		r := idx.SeriesIDs(tt.names, tt.filters)
		expectedIDs := influxdb.SeriesIDs(tt.result)
		if !r.Equals(expectedIDs) {
			t.Fatalf("%d: filters: %s: result mismatch:\n  exp=%s\n  got=%s", i, influxdb.Filters(tt.filters), expectedIDs, r)
		}
	}
}

func TestIndex_FieldKeys(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagKeys(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagKeysForMeasurement(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagValuesWhereFilter(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagValuesWhereFilterMultiple(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagValuesWhereNot(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_TagValuesWhereFilterAndNot(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_MeasurementsWhereFilter(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_DropSeries(t *testing.T) {
	t.Skip("pending")
}

func TestIndex_DropMeasurement(t *testing.T) {
	t.Skip("pending")
}

// indexWithFixtureData returns a populated Index for use in many of the filtering tests
func indexWithFixtureData() *influxdb.Index {
	idx := influxdb.NewIndex()
	s := &influxdb.Series{
		ID:   uint32(1),
		Tags: map[string]string{"host": "servera.influx.com", "region": "uswest"}}

	added := idx.AddSeries("cpu_load", s)
	if !added {
		return nil
	}

	s = &influxdb.Series{
		ID:   uint32(2),
		Tags: map[string]string{"host": "serverb.influx.com", "region": "uswest"}}

	added = idx.AddSeries("cpu_load", s)
	if !added {
		return nil
	}

	s = &influxdb.Series{
		ID:   uint32(3),
		Tags: map[string]string{"host": "serverc.influx.com", "region": "uswest", "service": "redis"}}

	added = idx.AddSeries("key_count", s)
	if !added {
		return nil
	}

	s = &influxdb.Series{
		ID:   uint32(4),
		Tags: map[string]string{"host": "serverd.influx.com", "region": "useast", "service": "redis"}}

	added = idx.AddSeries("key_count", s)
	if !added {
		return nil
	}

	s = &influxdb.Series{
		ID:   uint32(5),
		Tags: map[string]string{"name": "high priority"}}

	added = idx.AddSeries("queue_depth", s)
	if !added {
		return nil
	}

	return idx
}

func TestIndex_SeriesIDsIntersect(t *testing.T) {
	var tests = []struct {
		expected []uint32
		left     []uint32
		right    []uint32
	}{
		// both sets empty
		{
			expected: []uint32{},
			left:     []uint32{},
			right:    []uint32{},
		},

		// right set empty
		{
			expected: []uint32{},
			left:     []uint32{uint32(1)},
			right:    []uint32{},
		},

		// left set empty
		{
			expected: []uint32{},
			left:     []uint32{},
			right:    []uint32{uint32(1)},
		},

		// both sides same size
		{
			expected: []uint32{uint32(1), uint32(4)},
			left:     []uint32{uint32(1), uint32(2), uint32(4), uint32(5)},
			right:    []uint32{uint32(1), uint32(3), uint32(4), uint32(7)},
		},

		// left side bigger
		{
			expected: []uint32{uint32(2)},
			left:     []uint32{uint32(1), uint32(2), uint32(3)},
			right:    []uint32{uint32(2)},
		},

		// right side bigger
		{
			expected: []uint32{uint32(4), uint32(8)},
			left:     []uint32{uint32(2), uint32(3), uint32(4), uint32(8)},
			right:    []uint32{uint32(1), uint32(4), uint32(7), uint32(8), uint32(9)},
		},
	}

	for i, tt := range tests {
		a := influxdb.SeriesIDs(tt.left).Intersect(tt.right)
		if !a.Equals(tt.expected) {
			t.Fatalf("%d: %s intersect %s: result mismatch:\n  exp=%s\n  got=%s", i, influxdb.SeriesIDs(tt.left), influxdb.SeriesIDs(tt.right), influxdb.SeriesIDs(tt.expected), influxdb.SeriesIDs(a))
		}
	}
}
<|endoftext|>


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

// Command git-p prints the status of pending commits on the current
// branch.
//
// git-p summarizes the status of each commit, including its review
// state in Gerrit and whether or not there are any comments or TryBot
// failures.
package main

import (
	"fmt"
	"log"
	"strings"
	"unicode/utf8"
)

// TODO: Provide a way to exclude branches (like archive/, etc)

// TODO: Do the right thing if the terminal is dumb.

const (
	// TODO: Support other repos.
	remoteUrl = "https://go.googlesource.com/go"
	project   = "go"
	gerritUrl = "https://go-review.googlesource.com"
)

func main() {
	setupPager()

	// Find the Gerrit remote name.
	remote, err := getRemote(remoteUrl)
	if err != nil {
		log.Fatal(err)
	}

	// Get commits that are available from the Gerrit remote.
	upstreams := lines(git("for-each-ref", "--format", "%(objectname)", "refs/remotes/"+remote+"/"))
	if len(upstreams) == 0 {
		log.Fatalf("no refs for remote %s", remote)
	}

	gerrit := NewGerrit(gerritUrl)

	// Pass a token through each showBranch so we can pipeline
	// fetching branch information, while displaying it in order.
	token := make(chan struct{}, 1)
	token <- struct{}{}
	// But if the output of showBranch is blocked (e.g., by
	// back-pressure from a pager), don't start new showBranches.
	// This avoids making lots of ultimately ignored requests to
	// Gerrit.
	limit := make(chan struct{}, 3)

	// Resolve HEAD and show it first regardless of age.
	head, _ := tryGit("symbolic-ref", "HEAD")
	if head != "" {
		token = showBranch(gerrit, head, "HEAD", remote, upstreams, token, limit)
	}

	// Get all local branches, sorted by most recent commit date.
	branches := lines(git("for-each-ref", "--format", "%(refname)", "--sort", "-committerdate", "refs/heads/"))
	for _, branch := range branches {
		if branch == head {
			continue
		}
		token = showBranch(gerrit, branch, "", remote, upstreams, token, limit)
	}

	<-token
}

func showBranch(gerrit *Gerrit, branch, extra string, remote string, upstreams []string, token, limit chan struct{}) chan struct{} {
	// Don't start too many showBranches.
	limit <- struct{}{}

	// Get the Gerrit upstream name so we can construct full
	// Change-IDs.
	upstream := upstreamOf(branch)
	if upstream == "" {
		upstream = "refs/" + remote + "/master"
	}

	// Get commits from the branch to any upstream.
	args := []string{"rev-list", branch}
	for _, u := range upstreams {
		args = append(args, "^"+u)
	}
	commits := lines(git(args...))

	// Get Change-Ids from these commits.
	cids := changeIds(project, upstream, commits)

	// Fetch information on all of these changes.
	//
	// We need DETAILED_LABELS to get numeric values of labels.
	changes := make([]*GerritChanges, len(cids))
	for i, cid := range cids {
		// TODO: Would this be simpler with a single big OR query?
		if cid != "" {
			changes[i] = gerrit.QueryChanges("change:"+cid, printChangeOptions...)
		}
	}

	if len(changes) == 0 {
		<-limit
		return token
	}

	done := make(chan struct{})
	go func() {
		<-token
		// Print changes.
		fmt.Printf("\x1b[1;32m%s\x1b[0m", strings.TrimPrefix(branch, "refs/heads/"))
		if extra != "" {
			fmt.Printf(" (\x1b[1;36m%s\x1b[0m)", extra)
		}
		fmt.Printf("\n")
		for i, change := range changes {
			printChange(commits[i], change)
		}
		fmt.Println()
		<-limit
		done <- struct{}{}
	}()
	return done
}

func changeStatus(commit string, info *GerritChange) (string, []string) {
	switch info.Status {
	default:
		return fmt.Sprintf("Unknown status %q", info.Status), nil
	case "MERGED":
		return "Submitted", nil
	case "ABANDONED":
		return "Abandoned", nil
	case "DRAFT":
		return "Draft", nil
	case "NEW":
	}

	// Check for warnings on current PS. (Requires
	// CURRENT_REVISION or ALL_REVISIONS option.)
	warnings := []string{}
	curPatchSet := info.Revisions[info.CurrentRevision].Number
	// Are there unmailed changes?
	if info.CurrentRevision != commit {
		warnings = append(warnings, "Local commit differs from mailed commit")
	}
	// Are there comments on the latest PS? (Requires
	// MESSAGES option.)
	nComments := 0
	commentUsers, commentUsersSet := []string{}, map[string]bool{}
	for _, msg := range info.Messages {
		if msg.PatchSet != curPatchSet {
			continue
		}
		// Ignore automated comments.
		if strings.HasPrefix(msg.Tag, "autogenerated:gerrit:") {
			continue
		}
		// Ignore TryBot comments (Requires
		// DETAILED_ACCOUNTS option.)
		if msg.Author.Email == "gobot@golang.org" {
			continue
		}
		nComments++
		if !commentUsersSet[msg.Author.Name] {
			commentUsersSet[msg.Author.Name] = true
			commentUsers = append(commentUsers, msg.Author.Name)
		}
	}
	if nComments > 0 {
		msg := "1 comment"
		if nComments > 1 {
			msg = fmt.Sprintf("%d comments", nComments)
		}
		msg += " on latest PS from " + strings.Join(commentUsers, ", ")
		warnings = append(warnings, msg)
	}
	// Are the trybots unhappy? (Requires LABELS option.)
	if tbr := info.Labels["TryBot-Result"]; tbr != nil && tbr.Rejected != nil {
		// TODO: List failed configs
		warnings = append(warnings, "TryBots are unhappy")
	} else if tbr == nil || tbr.Approved == nil {
		warnings = append(warnings, "TryBots not run")
	}

	// Submittable? (Requires SUBMITTABLE option.)
	status := "Pending"
	if info.Submittable {
		status = "Ready"
	}

	return status, warnings
}

var printChangeOptions = []string{"SUBMITTABLE", "LABELS", "CURRENT_REVISION", "MESSAGES", "DETAILED_ACCOUNTS"}

var display = map[string]string{
	"Not mailed": "\x1b[4m", // Underline

	"Pending warn": "\x1b[1;33m", // Bright yellow
	"Ready warn":   "\x1b[1;33m", // Bright yellow

	"Ready": "\x1b[1;32m", // Bright green

	"Submitted": "\x1b[37m",   // Gray
	"Abandoned": "\x1b[9;37m", // Gray, strike-through
	"Draft":     "\x1b[37m",   // Gray
}

// printChange prints a summary of change's status and warnings.
//
// change must be retrieved with options printChangeOptions.
func printChange(commit string, change *GerritChanges) {
	logMsg := git("log", "-n1", "--oneline", commit)

	status, warnings, link := "Not mailed", []string(nil), ""
	if change != nil {
		results, err := change.Wait()
		if err != nil {
			log.Fatal(err)
		}
		if len(results) > 1 {
			log.Fatal("multiple changes found for commit %s", commit)
		}
		if len(results) == 1 {
			status, warnings = changeStatus(commit, results[0])
			//link = fmt.Sprintf("[%s/c/%d]", gerritUrl, results[0].Number)
			link = fmt.Sprintf(" [golang.org/cl/%d]", results[0].Number)
		}
	}

	var control, eControl string
	if len(warnings) != 0 {
		if c, ok := display[status+" warn"]; ok {
			control = c
		}
	}
	if control == "" {
		if c, ok := display[status]; ok {
			control = c
		}
	}
	if control != "" {
		eControl = "\x1b[0m"
	}

	hdr := fmt.Sprintf("%-10s %s", status, logMsg)
	hdrMax := 80 - len(link) - 2
	if utf8.RuneCountInString(hdr) > hdrMax {
		hdr = fmt.Sprintf("%*.*s…", hdrMax-1, hdrMax-1, hdr)
	}
	fmt.Printf("  %s%-*s%s%s\n", control, hdrMax, hdr, eControl, link)
	for _, w := range warnings {
		fmt.Printf("    %s\n", w)
	}
}
<commit_msg>git-p: accept branches on command line<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command git-p prints the status of pending commits on all branches.
//
// git-p summarizes the status of each commit, including its review
// state in Gerrit and whether or not there are any comments or TryBot
// failures.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"unicode/utf8"
)

// TODO: Provide a way to exclude branches (like archive/, etc)

// TODO: Do the right thing if the terminal is dumb.

const (
	// TODO: Support other repos.
	remoteUrl = "https://go.googlesource.com/go"
	project   = "go"
	gerritUrl = "https://go-review.googlesource.com"
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [branches...]\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "With no arguments, list branches from newest to oldest.\n")
		flag.PrintDefaults()
	}
	flag.Parse()
	branches := flag.Args()

	// Check the branch names.
	for _, b := range branches {
		if out, err := tryGit("rev-parse", b, "--"); err != nil {
			fmt.Printf("%s\n", out)
			os.Exit(1)
		}
	}

	setupPager()

	// Find the Gerrit remote name.
	remote, err := getRemote(remoteUrl)
	if err != nil {
		log.Fatal(err)
	}

	// Get commits that are available from the Gerrit remote.
	upstreams := lines(git("for-each-ref", "--format", "%(objectname)", "refs/remotes/"+remote+"/"))
	if len(upstreams) == 0 {
		log.Fatalf("no refs for remote %s", remote)
	}

	gerrit := NewGerrit(gerritUrl)

	// Pass a token through each showBranch so we can pipeline
	// fetching branch information, while displaying it in order.
	token := make(chan struct{}, 1)
	token <- struct{}{}
	// But if the output of showBranch is blocked (e.g., by
	// back-pressure from a pager), don't start new showBranches.
	// This avoids making lots of ultimately ignored requests to
	// Gerrit.
	limit := make(chan struct{}, 3)

	var head string
	if len(branches) == 0 {
		// Resolve HEAD and show it first regardless of age.
		head, _ = tryGit("symbolic-ref", "HEAD")
		if head != "" {
			token = showBranch(gerrit, head, "HEAD", remote, upstreams, token, limit)
		}

		// Get all local branches, sorted by most recent commit date.
		branches = lines(git("for-each-ref", "--format", "%(refname)", "--sort", "-committerdate", "refs/heads/"))
	}

	// Show all branches.
	for _, branch := range branches {
		if branch == head {
			continue
		}
		token = showBranch(gerrit, branch, "", remote, upstreams, token, limit)
	}

	<-token
}

func showBranch(gerrit *Gerrit, branch, extra string, remote string, upstreams []string, token, limit chan struct{}) chan struct{} {
	// Don't start too many showBranches.
	limit <- struct{}{}

	// Get the Gerrit upstream name so we can construct full
	// Change-IDs.
	upstream := upstreamOf(branch)
	if upstream == "" {
		upstream = "refs/" + remote + "/master"
	}

	// Get commits from the branch to any upstream.
	args := []string{"rev-list", branch}
	for _, u := range upstreams {
		args = append(args, "^"+u)
	}
	commits := lines(git(args...))

	// Get Change-Ids from these commits.
	cids := changeIds(project, upstream, commits)

	// Fetch information on all of these changes.
	//
	// We need DETAILED_LABELS to get numeric values of labels.
	changes := make([]*GerritChanges, len(cids))
	for i, cid := range cids {
		// TODO: Would this be simpler with a single big OR query?
		if cid != "" {
			changes[i] = gerrit.QueryChanges("change:"+cid, printChangeOptions...)
		}
	}

	if len(changes) == 0 {
		<-limit
		return token
	}

	done := make(chan struct{})
	go func() {
		<-token
		// Print changes.
		fmt.Printf("\x1b[1;32m%s\x1b[0m", strings.TrimPrefix(branch, "refs/heads/"))
		if extra != "" {
			fmt.Printf(" (\x1b[1;36m%s\x1b[0m)", extra)
		}
		fmt.Printf("\n")
		for i, change := range changes {
			printChange(commits[i], change)
		}
		fmt.Println()
		<-limit
		done <- struct{}{}
	}()
	return done
}

func changeStatus(commit string, info *GerritChange) (string, []string) {
	switch info.Status {
	default:
		return fmt.Sprintf("Unknown status %q", info.Status), nil
	case "MERGED":
		return "Submitted", nil
	case "ABANDONED":
		return "Abandoned", nil
	case "DRAFT":
		return "Draft", nil
	case "NEW":
	}

	// Check for warnings on current PS. (Requires
	// CURRENT_REVISION or ALL_REVISIONS option.)
	warnings := []string{}
	curPatchSet := info.Revisions[info.CurrentRevision].Number
	// Are there unmailed changes?
	if info.CurrentRevision != commit {
		warnings = append(warnings, "Local commit differs from mailed commit")
	}
	// Are there comments on the latest PS? (Requires
	// MESSAGES option.)
	nComments := 0
	commentUsers, commentUsersSet := []string{}, map[string]bool{}
	for _, msg := range info.Messages {
		if msg.PatchSet != curPatchSet {
			continue
		}
		// Ignore automated comments.
		if strings.HasPrefix(msg.Tag, "autogenerated:gerrit:") {
			continue
		}
		// Ignore TryBot comments (Requires
		// DETAILED_ACCOUNTS option.)
		if msg.Author.Email == "gobot@golang.org" {
			continue
		}
		nComments++
		if !commentUsersSet[msg.Author.Name] {
			commentUsersSet[msg.Author.Name] = true
			commentUsers = append(commentUsers, msg.Author.Name)
		}
	}
	if nComments > 0 {
		msg := "1 comment"
		if nComments > 1 {
			msg = fmt.Sprintf("%d comments", nComments)
		}
		msg += " on latest PS from " + strings.Join(commentUsers, ", ")
		warnings = append(warnings, msg)
	}
	// Are the trybots unhappy? (Requires LABELS option.)
	if tbr := info.Labels["TryBot-Result"]; tbr != nil && tbr.Rejected != nil {
		// TODO: List failed configs
		warnings = append(warnings, "TryBots are unhappy")
	} else if tbr == nil || tbr.Approved == nil {
		warnings = append(warnings, "TryBots not run")
	}

	// Submittable? (Requires SUBMITTABLE option.)
	status := "Pending"
	if info.Submittable {
		status = "Ready"
	}

	return status, warnings
}

var printChangeOptions = []string{"SUBMITTABLE", "LABELS", "CURRENT_REVISION", "MESSAGES", "DETAILED_ACCOUNTS"}

var display = map[string]string{
	"Not mailed": "\x1b[4m", // Underline

	"Pending warn": "\x1b[1;33m", // Bright yellow
	"Ready warn":   "\x1b[1;33m", // Bright yellow

	"Ready": "\x1b[1;32m", // Bright green

	"Submitted": "\x1b[37m",   // Gray
	"Abandoned": "\x1b[9;37m", // Gray, strike-through
	"Draft":     "\x1b[37m",   // Gray
}

// printChange prints a summary of change's status and warnings.
//
// change must be retrieved with options printChangeOptions.
func printChange(commit string, change *GerritChanges) {
	logMsg := git("log", "-n1", "--oneline", commit)

	status, warnings, link := "Not mailed", []string(nil), ""
	if change != nil {
		results, err := change.Wait()
		if err != nil {
			log.Fatal(err)
		}
		if len(results) > 1 {
			log.Fatal("multiple changes found for commit %s", commit)
		}
		if len(results) == 1 {
			status, warnings = changeStatus(commit, results[0])
			//link = fmt.Sprintf("[%s/c/%d]", gerritUrl, results[0].Number)
			link = fmt.Sprintf(" [golang.org/cl/%d]", results[0].Number)
		}
	}

	var control, eControl string
	if len(warnings) != 0 {
		if c, ok := display[status+" warn"]; ok {
			control = c
		}
	}
	if control == "" {
		if c, ok := display[status]; ok {
			control = c
		}
	}
	if control != "" {
		eControl = "\x1b[0m"
	}

	hdr := fmt.Sprintf("%-10s %s", status, logMsg)
	hdrMax := 80 - len(link) - 2
	if utf8.RuneCountInString(hdr) > hdrMax {
		hdr = fmt.Sprintf("%*.*s…", hdrMax-1, hdrMax-1, hdr)
	}
	fmt.Printf("  %s%-*s%s%s\n", control, hdrMax, hdr, eControl, link)
	for _, w := range warnings {
		fmt.Printf("    %s\n", w)
	}
}
<|endoftext|>


<commit_before>package atcclient

import (
	"errors"
	"fmt"
)

type UnexpectedResponseError struct {
	error
	StatusCode int
	Status     string
	Body       string
}

func (e UnexpectedResponseError) Error() string {
	return fmt.Sprintf("Unexpected Response\nStatus: %s\nBody:\n%s", e.Status, e.Body)
}

type ResourceNotFoundError struct {
	error
}

func (e ResourceNotFoundError) Error() string {
	return fmt.Sprintf("Resource Not Found")
}

func NameRequiredError(thing string) error {
	return errors.New(thing + "name required")
}
<commit_msg>add missing space<commit_after>package atcclient

import (
	"errors"
	"fmt"
)

type UnexpectedResponseError struct {
	error
	StatusCode int
	Status     string
	Body       string
}

func (e UnexpectedResponseError) Error() string {
	return fmt.Sprintf("Unexpected Response\nStatus: %s\nBody:\n%s", e.Status, e.Body)
}

type ResourceNotFoundError struct {
	error
}

func (e ResourceNotFoundError) Error() string {
	return fmt.Sprintf("Resource Not Found")
}

func NameRequiredError(thing string) error {
	return errors.New(thing + " name required")
}
<|endoftext|>


<commit_before>package atom_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"

	"github.com/mmcdole/gofeed/atom"
	"github.com/stretchr/testify/assert"
)

// Tests

func TestParser_Parse(t *testing.T) {
	files, _ := filepath.Glob("../testdata/parser/atom/*.xml")
	for _, f := range files {
		base := filepath.Base(f)
		name := strings.TrimSuffix(base, filepath.Ext(base))

		fmt.Printf("Testing %s... ", name)

		// Get actual source feed
		ff := fmt.Sprintf("../testdata/parser/atom/%s.xml", name)
		f, _ := ioutil.ReadFile(ff)

		// Parse actual feed
		fp := &atom.Parser{}
		actual, _ := fp.Parse(bytes.NewReader(f))

		// Get json encoded expected feed result
		ef := fmt.Sprintf("../testdata/parser/atom/%s.json", name)
		e, _ := ioutil.ReadFile(ef)

		// Unmarshal expected feed
		expected := &atom.Feed{}
		json.Unmarshal(e, &expected)

		if assert.Equal(t, actual, expected, "Feed file %s.xml did not match expected output %s.json", name, name) {
			fmt.Printf("OK\n")
		} else {
			fmt.Printf("Failed\n")
		}
	}
}

// TODO: Examples
<commit_msg>Fix transposed 'expected' and 'actual' arguments<commit_after>package atom_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"

	"github.com/mmcdole/gofeed/atom"
	"github.com/stretchr/testify/assert"
)

// Tests

func TestParser_Parse(t *testing.T) {
	files, _ := filepath.Glob("../testdata/parser/atom/*.xml")
	for _, f := range files {
		base := filepath.Base(f)
		name := strings.TrimSuffix(base, filepath.Ext(base))

		fmt.Printf("Testing %s... ", name)

		// Get actual source feed
		ff := fmt.Sprintf("../testdata/parser/atom/%s.xml", name)
		f, _ := ioutil.ReadFile(ff)

		// Parse actual feed
		fp := &atom.Parser{}
		actual, _ := fp.Parse(bytes.NewReader(f))

		// Get json encoded expected feed result
		ef := fmt.Sprintf("../testdata/parser/atom/%s.json", name)
		e, _ := ioutil.ReadFile(ef)

		// Unmarshal expected feed
		expected := &atom.Feed{}
		json.Unmarshal(e, expected)

		if assert.Equal(t, expected, actual, "Feed file %s.xml did not match expected output %s.json", name, name) {
			fmt.Printf("OK\n")
		} else {
			fmt.Printf("Failed\n")
		}
	}
}

// TODO: Examples
<|endoftext|>


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

package gitlab

import (
	"fmt"
	"time"
)

// GroupMilestonesService handles communication with the milestone related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_milestones.html
type GroupMilestonesService struct {
	client *Client
}

// GroupMilestone represents a GitLab milestone.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_milestones.html
type GroupMilestone struct {
	ID          int        `json:"id"`
	IID         int        `json:"iid"`
	GroupID     int        `json:"group_id"`
	Title       string     `json:"title"`
	Description string     `json:"description"`
	StartDate   *ISOTime   `json:"start_date"`
	DueDate     *ISOTime   `json:"due_date"`
	State       string     `json:"state"`
	UpdatedAt   *time.Time `json:"updated_at"`
	CreatedAt   *time.Time `json:"created_at"`
}

func (m GroupMilestone) String() string {
	return Stringify(m)
}

// ListGroupMilestonesOptions represents the available
// ListGroupMilestones() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones
type ListGroupMilestonesOptions struct {
	ListOptions
	IIDs   []int  `url:"iids,omitempty" json:"iids,omitempty"`
	State  string `url:"state,omitempty" json:"state,omitempty"`
	Search string `url:"search,omitempty" json:"search,omitempty"`
}

// ListGroupMilestones returns a list of group milestones.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones
func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListGroupMilestonesOptions, options ...RequestOptionFunc) ([]*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones", pathEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var m []*GroupMilestone
	resp, err := s.client.Do(req, &m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// GetGroupMilestone gets a single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-single-milestone
func (s *GroupMilestonesService) GetGroupMilestone(gid interface{}, milestone int, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d", pathEscape(group), milestone)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(GroupMilestone)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// CreateGroupMilestoneOptions represents the available CreateGroupMilestone() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#create-new-milestone
type CreateGroupMilestoneOptions struct {
	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
	Description *string  `url:"description,omitempty" json:"description,omitempty"`
	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
}

// CreateGroupMilestone creates a new group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#create-new-milestone
func (s *GroupMilestonesService) CreateGroupMilestone(gid interface{}, opt *CreateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones", pathEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(GroupMilestone)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// UpdateGroupMilestoneOptions represents the available UpdateGroupMilestone() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#edit-milestone
type UpdateGroupMilestoneOptions struct {
	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
	Description *string  `url:"description,omitempty" json:"description,omitempty"`
	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
	StateEvent  *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
}

// UpdateGroupMilestone updates an existing group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#edit-milestone
func (s *GroupMilestonesService) UpdateGroupMilestone(gid interface{}, milestone int, opt *UpdateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d", pathEscape(group), milestone)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(GroupMilestone)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// GetGroupMilestoneIssuesOptions represents the available GetGroupMilestoneIssues() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
type GetGroupMilestoneIssuesOptions ListOptions

// GetGroupMilestoneIssues gets all issues assigned to a single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
func (s *GroupMilestonesService) GetGroupMilestoneIssues(gid interface{}, milestone int, opt *GetGroupMilestoneIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d/issues", pathEscape(group), milestone)

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var i []*Issue
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, err
}

// GetGroupMilestoneMergeRequestsOptions represents the available
// GetGroupMilestoneMergeRequests() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
type GetGroupMilestoneMergeRequestsOptions ListOptions

// GetGroupMilestoneMergeRequests gets all merge requests assigned to a
// single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{}, milestone int, opt *GetGroupMilestoneMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d/merge_requests", pathEscape(group), milestone)

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var mr []*MergeRequest
	resp, err := s.client.Do(req, &mr)
	if err != nil {
		return nil, resp, err
	}

	return mr, resp, err
}
<commit_msg>Add group milestone burndown events API<commit_after>//
// Copyright 2018, Sander van Harmelen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"fmt"
	"time"
)

// GroupMilestonesService handles communication with the milestone related
// methods of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_milestones.html
type GroupMilestonesService struct {
	client *Client
}

// GroupMilestone represents a GitLab milestone.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/group_milestones.html
type GroupMilestone struct {
	ID          int        `json:"id"`
	IID         int        `json:"iid"`
	GroupID     int        `json:"group_id"`
	Title       string     `json:"title"`
	Description string     `json:"description"`
	StartDate   *ISOTime   `json:"start_date"`
	DueDate     *ISOTime   `json:"due_date"`
	State       string     `json:"state"`
	UpdatedAt   *time.Time `json:"updated_at"`
	CreatedAt   *time.Time `json:"created_at"`
}

func (m GroupMilestone) String() string {
	return Stringify(m)
}

// ListGroupMilestonesOptions represents the available
// ListGroupMilestones() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones
type ListGroupMilestonesOptions struct {
	ListOptions
	IIDs   []int  `url:"iids,omitempty" json:"iids,omitempty"`
	State  string `url:"state,omitempty" json:"state,omitempty"`
	Search string `url:"search,omitempty" json:"search,omitempty"`
}

// ListGroupMilestones returns a list of group milestones.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones
func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListGroupMilestonesOptions, options ...RequestOptionFunc) ([]*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones", pathEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var m []*GroupMilestone
	resp, err := s.client.Do(req, &m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// GetGroupMilestone gets a single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-single-milestone
func (s *GroupMilestonesService) GetGroupMilestone(gid interface{}, milestone int, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d", pathEscape(group), milestone)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(GroupMilestone)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// CreateGroupMilestoneOptions represents the available CreateGroupMilestone() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#create-new-milestone
type CreateGroupMilestoneOptions struct {
	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
	Description *string  `url:"description,omitempty" json:"description,omitempty"`
	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
}

// CreateGroupMilestone creates a new group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#create-new-milestone
func (s *GroupMilestonesService) CreateGroupMilestone(gid interface{}, opt *CreateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones", pathEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(GroupMilestone)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// UpdateGroupMilestoneOptions represents the available UpdateGroupMilestone() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#edit-milestone
type UpdateGroupMilestoneOptions struct {
	Title       *string  `url:"title,omitempty" json:"title,omitempty"`
	Description *string  `url:"description,omitempty" json:"description,omitempty"`
	StartDate   *ISOTime `url:"start_date,omitempty" json:"start_date,omitempty"`
	DueDate     *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
	StateEvent  *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
}

// UpdateGroupMilestone updates an existing group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#edit-milestone
func (s *GroupMilestonesService) UpdateGroupMilestone(gid interface{}, milestone int, opt *UpdateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d", pathEscape(group), milestone)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	m := new(GroupMilestone)
	resp, err := s.client.Do(req, m)
	if err != nil {
		return nil, resp, err
	}

	return m, resp, err
}

// GetGroupMilestoneIssuesOptions represents the available GetGroupMilestoneIssues() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
type GetGroupMilestoneIssuesOptions ListOptions

// GetGroupMilestoneIssues gets all issues assigned to a single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone
func (s *GroupMilestonesService) GetGroupMilestoneIssues(gid interface{}, milestone int, opt *GetGroupMilestoneIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d/issues", pathEscape(group), milestone)

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var i []*Issue
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, err
}

// GetGroupMilestoneMergeRequestsOptions represents the available
// GetGroupMilestoneMergeRequests() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
type GetGroupMilestoneMergeRequestsOptions ListOptions

// GetGroupMilestoneMergeRequests gets all merge requests assigned to a
// single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone
func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{}, milestone int, opt *GetGroupMilestoneMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d/merge_requests", pathEscape(group), milestone)

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var mr []*MergeRequest
	resp, err := s.client.Do(req, &mr)
	if err != nil {
		return nil, resp, err
	}

	return mr, resp, err
}

type BurndownChartEvent struct {
	CreatedAt *ISOTime `json:"created_at"`
	Weight    *int     `json:"weight"`
	Action    *string  `json:"action"`
}

// GetGroupMilestoneBurndownChartEventsOptions represents the available
// GetGroupMilestoneBurndownChartEventsOptions() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-burndown-chart-events-for-a-single-milestone-starter
type GetGroupMilestoneBurndownChartEventsOptions ListOptions

// GetGroupMilestoneBurndownChartEvents gets all merge requests assigned to a
// single group milestone.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-burndown-chart-events-for-a-single-milestone-starter
func (s *GroupMilestonesService) GetGroupMilestoneBurndownChartEvents(gid interface{}, milestone int, opt *GetGroupMilestoneBurndownChartEventsOptions, options ...RequestOptionFunc) ([]*BurndownChartEvents, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/milestones/%d/burndown_events", pathEscape(group), milestone)

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var be []*BurndownChartEvent
	resp, err := s.client.Do(req, &be)
	if err != nil {
		return nil, resp, err
	}

	return be, resp, err
}
<|endoftext|>