<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.

// The go-contrib-init command helps new Go contributors get their development
// environment set up for the Go contribution process.
//
// It aims to be a complement or alternative to https://golang.org/doc/contribute.html.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

var (
	repo = flag.String("repo", detectrepo(), "Which go repo you want to contribute to. Use \"go\" for the core, or e.g. \"net\" for golang.org/x/net/*")
	dry  = flag.Bool("dry-run", false, "Fail with problems instead of trying to fix things.")
)

func main() {
	log.SetFlags(0)
	flag.Parse()

	checkCLA()
	checkGoroot()
	checkWorkingDir()
	checkGitOrigin()
	checkGitCodeReview()
	fmt.Print("All good. Happy hacking!\n" +
		"Remember to squash your revised commits and preserve the magic Change-Id lines.\n" +
		"Next steps: https://golang.org/doc/contribute.html#commit_changes\n")
}

func detectrepo() string {
	wd, err := os.Getwd()
	if err != nil {
		return "go"
	}

	for _, path := range filepath.SplitList(build.Default.GOPATH) {
		rightdir := filepath.Join(path, "src", "golang.org", "x")
		if strings.HasPrefix(wd, rightdir) {
			tail := wd[len(rightdir)+1:]
			end := strings.Index(tail, string(os.PathSeparator))
			if end > 0 {
				repo := tail[:end]
				return repo
			}
		}
	}

	return "go"
}

func checkCLA() {
	slurp, err := ioutil.ReadFile(cookiesFile())
	if err != nil && !os.IsNotExist(err) {
		log.Fatal(err)
	}
	if bytes.Contains(slurp, []byte("go.googlesource.com")) &&
		bytes.Contains(slurp, []byte("go-review.googlesource.com")) {
		// Probably good.
		return
	}
	log.Fatal("Your .gitcookies file isn't configured.\n" +
		"Next steps:\n" +
		"  * Submit a CLA (https://golang.org/doc/contribute.html#cla) if not done\n" +
		"  * Go to https://go.googlesource.com/ and click \"Generate Password\" at the top,\n" +
		"    then follow instructions.\n" +
		"  * Run go-contrib-init again.\n")
}

func expandUser(s string) string {
	env := "HOME"
	if runtime.GOOS == "windows" {
		env = "USERPROFILE"
	} else if runtime.GOOS == "plan9" {
		env = "home"
	}
	home := os.Getenv(env)
	if home == "" {
		return s
	}

	if len(s) >= 2 && s[0] == '~' && os.IsPathSeparator(s[1]) {
		if runtime.GOOS == "windows" {
			s = filepath.ToSlash(filepath.Join(home, s[2:]))
		} else {
			s = filepath.Join(home, s[2:])
		}
	}
	return os.Expand(s, func(env string) string {
		if env == "HOME" {
			return home
		}
		return os.Getenv(env)
	})
}

func cookiesFile() string {
	out, _ := exec.Command("git", "config", "http.cookiefile").Output()
	if s := strings.TrimSpace(string(out)); s != "" {
		if strings.HasPrefix(s, "~") {
			s = expandUser(s)
		}
		return s
	}
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("USERPROFILE"), ".gitcookies")
	}
	return filepath.Join(os.Getenv("HOME"), ".gitcookies")
}

func checkGoroot() {
	v := os.Getenv("GOROOT")
	if v == "" {
		return
	}
	if *repo == "go" {
		if strings.HasPrefix(v, "/usr/") {
			log.Fatalf("Your GOROOT environment variable is set to %q\n"+
				"This is almost certainly not what you want. Either unset\n"+
				"your GOROOT or set it to the path of your development version\n"+
				"of Go.", v)
		}
		slurp, err := ioutil.ReadFile(filepath.Join(v, "VERSION"))
		if err == nil {
			slurp = bytes.TrimSpace(slurp)
			log.Fatalf("Your GOROOT environment variable is set to %q\n"+
				"But that path is to a binary release of Go, with VERSION file %q.\n"+
				"You should hack on Go in a fresh checkout of Go. Fix or unset your GOROOT.\n",
				v, slurp)
		}
	}
}

func checkWorkingDir() {
	if *repo == "go" {
		if inGoPath() {
			log.Fatal("You can't work on Go from within your GOPATH. Please checkout Go outside of your GOPATH")
		}
		return
	}
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	gopath := firstGoPath()
	if gopath == "" {
		log.Fatal("Your GOPATH is not set, please set it")
	}

	rightdir := filepath.Join(gopath, "src", "golang.org", "x", *repo)
	if !strings.HasPrefix(wd, rightdir) {
		dirExists, err := exists(rightdir)
		if err != nil {
			log.Fatal(err)
		}
		if !dirExists {
			log.Fatalf("The repo you want to work on is currently not on your system.\n"+
				"Run %q to obtain this repo\n"+
				"then go to the directory %q\n",
				"go get -d golang.org/x/"+*repo, rightdir)
		}
		log.Fatalf("Your current directory is:%q\n"+
			"Working on golang/x/%v requires you be in %q\n",
			wd, *repo, rightdir)
	}
}

func firstGoPath() string {
	list := filepath.SplitList(build.Default.GOPATH)
	if len(list) < 1 {
		return ""
	}
	return list[0]
}

func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func inGoPath() bool {
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	if os.Getenv("GOPATH") == "" {
		return false
	}

	for _, path := range filepath.SplitList(os.Getenv("GOPATH")) {
		if strings.HasPrefix(wd, filepath.Join(path, "src")) {
			return true
		}
	}

	return false
}

// mostly check that they didn't clone from github
func checkGitOrigin() {
	if _, err := exec.LookPath("git"); err != nil {
		log.Fatalf("You don't appear to have git installed. Do that.")
	}
	wantRemote := "https://go.googlesource.com/" + *repo
	remotes, err := exec.Command("git", "remote", "-v").Output()
	if err != nil {
		msg := cmdErr(err)
		if strings.Contains(msg, "Not a git repository") {
			log.Fatalf("Your current directory is not in a git checkout of %s", wantRemote)
		}
		log.Fatalf("Error running git remote -v: %v", msg)
	}
	matches := 0
	for _, line := range strings.Split(string(remotes), "\n") {
		line = strings.TrimSpace(line)
		if !strings.HasPrefix(line, "origin") {
			continue
		}
		if !strings.Contains(line, wantRemote) {
			curRemote := strings.Fields(strings.TrimPrefix(line, "origin"))[0]
			// TODO: if not in dryRun mode, just fix it?
			log.Fatalf("Current directory's git was cloned from %q; origin should be %q", curRemote, wantRemote)
		}
		matches++
	}
	if matches == 0 {
		log.Fatalf("git remote -v output didn't contain expected %q. Got:\n%s", wantRemote, remotes)
	}
}

func cmdErr(err error) string {
	if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
		return fmt.Sprintf("%s: %s", err, ee.Stderr)
	}
	return fmt.Sprint(err)
}

func checkGitCodeReview() {
	if _, err := exec.LookPath("git-codereview"); err != nil {
		if *dry {
			log.Fatalf("You don't appear to have git-codereview tool. While this is technically optional,\n" +
				"almost all Go contributors use it. Our documentation and this tool assume it is used.\n" +
				"To install it, run:\n\n\t$ go get golang.org/x/review/git-codereview\n\n(Then run go-contrib-init again)")
		}
		err := exec.Command("go", "get", "golang.org/x/review/git-codereview").Run()
		if err != nil {
			log.Printf("Error running go get golang.org/x/review/git-codereview: %v", cmdErr(err))
		}
		log.Printf("Installed git-codereview (ran `go get golang.org/x/review/git-codereview`)")
	}
	missing := false
	for _, cmd := range []string{"change", "gofmt", "mail", "pending", "submit", "sync"} {
		v, _ := exec.Command("git", "config", "alias."+cmd).Output()
		if strings.Contains(string(v), "codereview") {
			continue
		}
		if *dry {
			log.Printf("Missing alias. Run:\n\t$ git config alias.%s \"codereview %s\"", cmd, cmd)
			missing = true
		} else {
			err := exec.Command("git", "config", "alias."+cmd, "codereview "+cmd).Run()
			if err != nil {
				log.Fatalf("Error setting alias.%s: %v", cmd, cmdErr(err))
			}
		}
	}
	if missing {
		log.Fatalf("Missing aliases. (While optional, this tool assumes you use them.)")
	}
}
<commit_msg>cmd/go-contrib-init: match subdomain-less .gitcookies file<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.

// The go-contrib-init command helps new Go contributors get their development
// environment set up for the Go contribution process.
//
// It aims to be a complement or alternative to https://golang.org/doc/contribute.html.
package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

var (
	repo = flag.String("repo", detectrepo(), "Which go repo you want to contribute to. Use \"go\" for the core, or e.g. \"net\" for golang.org/x/net/*")
	dry  = flag.Bool("dry-run", false, "Fail with problems instead of trying to fix things.")
)

func main() {
	log.SetFlags(0)
	flag.Parse()

	checkCLA()
	checkGoroot()
	checkWorkingDir()
	checkGitOrigin()
	checkGitCodeReview()
	fmt.Print("All good. Happy hacking!\n" +
		"Remember to squash your revised commits and preserve the magic Change-Id lines.\n" +
		"Next steps: https://golang.org/doc/contribute.html#commit_changes\n")
}

func detectrepo() string {
	wd, err := os.Getwd()
	if err != nil {
		return "go"
	}

	for _, path := range filepath.SplitList(build.Default.GOPATH) {
		rightdir := filepath.Join(path, "src", "golang.org", "x")
		if strings.HasPrefix(wd, rightdir) {
			tail := wd[len(rightdir)+1:]
			end := strings.Index(tail, string(os.PathSeparator))
			if end > 0 {
				repo := tail[:end]
				return repo
			}
		}
	}

	return "go"
}

var googleSourceRx = regexp.MustCompile(`(?m)^(go|go-review)?\.googlesource.com\b`)

func checkCLA() {
	slurp, err := ioutil.ReadFile(cookiesFile())
	if err != nil && !os.IsNotExist(err) {
		log.Fatal(err)
	}
	if googleSourceRx.Match(slurp) {
		// Probably good.
		return
	}
	log.Fatal("Your .gitcookies file isn't configured.\n" +
		"Next steps:\n" +
		"  * Submit a CLA (https://golang.org/doc/contribute.html#cla) if not done\n" +
		"  * Go to https://go.googlesource.com/ and click \"Generate Password\" at the top,\n" +
		"    then follow instructions.\n" +
		"  * Run go-contrib-init again.\n")
}

func expandUser(s string) string {
	env := "HOME"
	if runtime.GOOS == "windows" {
		env = "USERPROFILE"
	} else if runtime.GOOS == "plan9" {
		env = "home"
	}
	home := os.Getenv(env)
	if home == "" {
		return s
	}

	if len(s) >= 2 && s[0] == '~' && os.IsPathSeparator(s[1]) {
		if runtime.GOOS == "windows" {
			s = filepath.ToSlash(filepath.Join(home, s[2:]))
		} else {
			s = filepath.Join(home, s[2:])
		}
	}
	return os.Expand(s, func(env string) string {
		if env == "HOME" {
			return home
		}
		return os.Getenv(env)
	})
}

func cookiesFile() string {
	out, _ := exec.Command("git", "config", "http.cookiefile").Output()
	if s := strings.TrimSpace(string(out)); s != "" {
		if strings.HasPrefix(s, "~") {
			s = expandUser(s)
		}
		return s
	}
	if runtime.GOOS == "windows" {
		return filepath.Join(os.Getenv("USERPROFILE"), ".gitcookies")
	}
	return filepath.Join(os.Getenv("HOME"), ".gitcookies")
}

func checkGoroot() {
	v := os.Getenv("GOROOT")
	if v == "" {
		return
	}
	if *repo == "go" {
		if strings.HasPrefix(v, "/usr/") {
			log.Fatalf("Your GOROOT environment variable is set to %q\n"+
				"This is almost certainly not what you want. Either unset\n"+
				"your GOROOT or set it to the path of your development version\n"+
				"of Go.", v)
		}
		slurp, err := ioutil.ReadFile(filepath.Join(v, "VERSION"))
		if err == nil {
			slurp = bytes.TrimSpace(slurp)
			log.Fatalf("Your GOROOT environment variable is set to %q\n"+
				"But that path is to a binary release of Go, with VERSION file %q.\n"+
				"You should hack on Go in a fresh checkout of Go. Fix or unset your GOROOT.\n",
				v, slurp)
		}
	}
}

func checkWorkingDir() {
	if *repo == "go" {
		if inGoPath() {
			log.Fatal("You can't work on Go from within your GOPATH. Please checkout Go outside of your GOPATH")
		}
		return
	}
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	gopath := firstGoPath()
	if gopath == "" {
		log.Fatal("Your GOPATH is not set, please set it")
	}

	rightdir := filepath.Join(gopath, "src", "golang.org", "x", *repo)
	if !strings.HasPrefix(wd, rightdir) {
		dirExists, err := exists(rightdir)
		if err != nil {
			log.Fatal(err)
		}
		if !dirExists {
			log.Fatalf("The repo you want to work on is currently not on your system.\n"+
				"Run %q to obtain this repo\n"+
				"then go to the directory %q\n",
				"go get -d golang.org/x/"+*repo, rightdir)
		}
		log.Fatalf("Your current directory is:%q\n"+
			"Working on golang/x/%v requires you be in %q\n",
			wd, *repo, rightdir)
	}
}

func firstGoPath() string {
	list := filepath.SplitList(build.Default.GOPATH)
	if len(list) < 1 {
		return ""
	}
	return list[0]
}

func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func inGoPath() bool {
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	if os.Getenv("GOPATH") == "" {
		return false
	}

	for _, path := range filepath.SplitList(os.Getenv("GOPATH")) {
		if strings.HasPrefix(wd, filepath.Join(path, "src")) {
			return true
		}
	}

	return false
}

// mostly check that they didn't clone from github
func checkGitOrigin() {
	if _, err := exec.LookPath("git"); err != nil {
		log.Fatalf("You don't appear to have git installed. Do that.")
	}
	wantRemote := "https://go.googlesource.com/" + *repo
	remotes, err := exec.Command("git", "remote", "-v").Output()
	if err != nil {
		msg := cmdErr(err)
		if strings.Contains(msg, "Not a git repository") {
			log.Fatalf("Your current directory is not in a git checkout of %s", wantRemote)
		}
		log.Fatalf("Error running git remote -v: %v", msg)
	}
	matches := 0
	for _, line := range strings.Split(string(remotes), "\n") {
		line = strings.TrimSpace(line)
		if !strings.HasPrefix(line, "origin") {
			continue
		}
		if !strings.Contains(line, wantRemote) {
			curRemote := strings.Fields(strings.TrimPrefix(line, "origin"))[0]
			// TODO: if not in dryRun mode, just fix it?
			log.Fatalf("Current directory's git was cloned from %q; origin should be %q", curRemote, wantRemote)
		}
		matches++
	}
	if matches == 0 {
		log.Fatalf("git remote -v output didn't contain expected %q. Got:\n%s", wantRemote, remotes)
	}
}

func cmdErr(err error) string {
	if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
		return fmt.Sprintf("%s: %s", err, ee.Stderr)
	}
	return fmt.Sprint(err)
}

func checkGitCodeReview() {
	if _, err := exec.LookPath("git-codereview"); err != nil {
		if *dry {
			log.Fatalf("You don't appear to have git-codereview tool. While this is technically optional,\n" +
				"almost all Go contributors use it. Our documentation and this tool assume it is used.\n" +
				"To install it, run:\n\n\t$ go get golang.org/x/review/git-codereview\n\n(Then run go-contrib-init again)")
		}
		err := exec.Command("go", "get", "golang.org/x/review/git-codereview").Run()
		if err != nil {
			log.Printf("Error running go get golang.org/x/review/git-codereview: %v", cmdErr(err))
		}
		log.Printf("Installed git-codereview (ran `go get golang.org/x/review/git-codereview`)")
	}
	missing := false
	for _, cmd := range []string{"change", "gofmt", "mail", "pending", "submit", "sync"} {
		v, _ := exec.Command("git", "config", "alias."+cmd).Output()
		if strings.Contains(string(v), "codereview") {
			continue
		}
		if *dry {
			log.Printf("Missing alias. Run:\n\t$ git config alias.%s \"codereview %s\"", cmd, cmd)
			missing = true
		} else {
			err := exec.Command("git", "config", "alias."+cmd, "codereview "+cmd).Run()
			if err != nil {
				log.Fatalf("Error setting alias.%s: %v", cmd, cmdErr(err))
			}
		}
	}
	if missing {
		log.Fatalf("Missing aliases. (While optional, this tool assumes you use them.)")
	}
}
<|endoftext|>


<commit_before>package slack

import (
	"fmt"
	"time"
)

// UserPrefs needs to be implemented
type UserPrefs struct {
	// "highlight_words":"",
	// "user_colors":"",
	// "color_names_in_list":true,
	// "growls_enabled":true,
	// "tz":"Europe\/London",
	// "push_dm_alert":true,
	// "push_mention_alert":true,
	// "push_everything":true,
	// "push_idle_wait":2,
	// "push_sound":"b2.mp3",
	// "push_loud_channels":"",
	// "push_mention_channels":"",
	// "push_loud_channels_set":"",
	// "email_alerts":"instant",
	// "email_alerts_sleep_until":0,
	// "email_misc":false,
	// "email_weekly":true,
	// "welcome_message_hidden":false,
	// "all_channels_loud":true,
	// "loud_channels":"",
	// "never_channels":"",
	// "loud_channels_set":"",
	// "show_member_presence":true,
	// "search_sort":"timestamp",
	// "expand_inline_imgs":true,
	// "expand_internal_inline_imgs":true,
	// "expand_snippets":false,
	// "posts_formatting_guide":true,
	// "seen_welcome_2":true,
	// "seen_ssb_prompt":false,
	// "search_only_my_channels":false,
	// "emoji_mode":"default",
	// "has_invited":true,
	// "has_uploaded":false,
	// "has_created_channel":true,
	// "search_exclude_channels":"",
	// "messages_theme":"default",
	// "webapp_spellcheck":true,
	// "no_joined_overlays":false,
	// "no_created_overlays":true,
	// "dropbox_enabled":false,
	// "seen_user_menu_tip_card":true,
	// "seen_team_menu_tip_card":true,
	// "seen_channel_menu_tip_card":true,
	// "seen_message_input_tip_card":true,
	// "seen_channels_tip_card":true,
	// "seen_domain_invite_reminder":false,
	// "seen_member_invite_reminder":false,
	// "seen_flexpane_tip_card":true,
	// "seen_search_input_tip_card":true,
	// "mute_sounds":false,
	// "arrow_history":false,
	// "tab_ui_return_selects":true,
	// "obey_inline_img_limit":true,
	// "new_msg_snd":"knock_brush.mp3",
	// "collapsible":false,
	// "collapsible_by_click":true,
	// "require_at":false,
	// "mac_ssb_bounce":"",
	// "mac_ssb_bullet":true,
	// "win_ssb_bullet":true,
	// "expand_non_media_attachments":true,
	// "show_typing":true,
	// "pagekeys_handled":true,
	// "last_snippet_type":"",
	// "display_real_names_override":0,
	// "time24":false,
	// "enter_is_special_in_tbt":false,
	// "graphic_emoticons":false,
	// "convert_emoticons":true,
	// "autoplay_chat_sounds":true,
	// "ss_emojis":true,
	// "sidebar_behavior":"",
	// "mark_msgs_read_immediately":true,
	// "start_scroll_at_oldest":true,
	// "snippet_editor_wrap_long_lines":false,
	// "ls_disabled":false,
	// "sidebar_theme":"default",
	// "sidebar_theme_custom_values":"",
	// "f_key_search":false,
	// "k_key_omnibox":true,
	// "speak_growls":false,
	// "mac_speak_voice":"com.apple.speech.synthesis.voice.Alex",
	// "mac_speak_speed":250,
	// "comma_key_prefs":false,
	// "at_channel_suppressed_channels":"",
	// "push_at_channel_suppressed_channels":"",
	// "prompted_for_email_disabling":false,
	// "full_text_extracts":false,
	// "no_text_in_notifications":false,
	// "muted_channels":"",
	// "no_macssb1_banner":false,
	// "privacy_policy_seen":true,
	// "search_exclude_bots":false,
	// "fuzzy_matching":false
}

// UserDetails contains user details coming in the initial response from StartRTM
type UserDetails struct {
	ID             string    `json:"id"`
	Name           string    `json:"name"`
	Created        JSONTime  `json:"created"`
	ManualPresence string    `json:"manual_presence"`
	Prefs          UserPrefs `json:"prefs"`
}

// JSONTime exists so that we can have a String method converting the date
type JSONTime int64

// String converts the unix timestamp into a string
func (t JSONTime) String() string {
	tm := t.Time()
	return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2"))
}

// Time returns a `time.Time` representation of this value.
func (t JSONTime) Time() time.Time {
	return time.Unix(int64(t), 0)
}

// Team contains details about a team
type Team struct {
	ID     string `json:"id"`
	Name   string `json:"name"`
	Domain string `json:"domain"`
}

// Icons XXX: needs further investigation
type Icons struct {
	Image36 string `json:"image_36,omitempty"`
	Image48 string `json:"image_48,omitempty"`
	Image72 string `json:"image_72,omitempty"`
}

// Info contains various details about Users, Channels, Bots and the authenticated user.
// It is returned by StartRTM or included in the "ConnectedEvent" RTM event.
type Info struct {
	URL      string       `json:"url,omitempty"`
	User     *UserDetails `json:"self,omitempty"`
	Team     *Team        `json:"team,omitempty"`
	Users    []User       `json:"users,omitempty"`
	Channels []Channel    `json:"channels,omitempty"`
	Groups   []Group      `json:"groups,omitempty"`
	Bots     []Bot        `json:"bots,omitempty"`
	IMs      []IM         `json:"ims,omitempty"`
}

type infoResponseFull struct {
	Info
	WebResponse
}

// GetBotByID returns a bot given a bot id
func (info Info) GetBotByID(botID string) *Bot {
	for _, bot := range info.Bots {
		if bot.ID == botID {
			return &bot
		}
	}
	return nil
}

// GetUserByID returns a user given a user id
func (info Info) GetUserByID(userID string) *User {
	for _, user := range info.Users {
		if user.ID == userID {
			return &user
		}
	}
	return nil
}

// GetChannelByID returns a channel given a channel id
func (info Info) GetChannelByID(channelID string) *Channel {
	for _, channel := range info.Channels {
		if channel.ID == channelID {
			return &channel
		}
	}
	return nil
}

// GetGroupByID returns a group given a group id
func (info Info) GetGroupByID(groupID string) *Group {
	for _, group := range info.Groups {
		if group.ID == groupID {
			return &group
		}
	}
	return nil
}
<commit_msg>Implement method `GetIMByID()` on struct `Info`<commit_after>package slack

import (
	"fmt"
	"time"
)

// UserPrefs needs to be implemented
type UserPrefs struct {
	// "highlight_words":"",
	// "user_colors":"",
	// "color_names_in_list":true,
	// "growls_enabled":true,
	// "tz":"Europe\/London",
	// "push_dm_alert":true,
	// "push_mention_alert":true,
	// "push_everything":true,
	// "push_idle_wait":2,
	// "push_sound":"b2.mp3",
	// "push_loud_channels":"",
	// "push_mention_channels":"",
	// "push_loud_channels_set":"",
	// "email_alerts":"instant",
	// "email_alerts_sleep_until":0,
	// "email_misc":false,
	// "email_weekly":true,
	// "welcome_message_hidden":false,
	// "all_channels_loud":true,
	// "loud_channels":"",
	// "never_channels":"",
	// "loud_channels_set":"",
	// "show_member_presence":true,
	// "search_sort":"timestamp",
	// "expand_inline_imgs":true,
	// "expand_internal_inline_imgs":true,
	// "expand_snippets":false,
	// "posts_formatting_guide":true,
	// "seen_welcome_2":true,
	// "seen_ssb_prompt":false,
	// "search_only_my_channels":false,
	// "emoji_mode":"default",
	// "has_invited":true,
	// "has_uploaded":false,
	// "has_created_channel":true,
	// "search_exclude_channels":"",
	// "messages_theme":"default",
	// "webapp_spellcheck":true,
	// "no_joined_overlays":false,
	// "no_created_overlays":true,
	// "dropbox_enabled":false,
	// "seen_user_menu_tip_card":true,
	// "seen_team_menu_tip_card":true,
	// "seen_channel_menu_tip_card":true,
	// "seen_message_input_tip_card":true,
	// "seen_channels_tip_card":true,
	// "seen_domain_invite_reminder":false,
	// "seen_member_invite_reminder":false,
	// "seen_flexpane_tip_card":true,
	// "seen_search_input_tip_card":true,
	// "mute_sounds":false,
	// "arrow_history":false,
	// "tab_ui_return_selects":true,
	// "obey_inline_img_limit":true,
	// "new_msg_snd":"knock_brush.mp3",
	// "collapsible":false,
	// "collapsible_by_click":true,
	// "require_at":false,
	// "mac_ssb_bounce":"",
	// "mac_ssb_bullet":true,
	// "win_ssb_bullet":true,
	// "expand_non_media_attachments":true,
	// "show_typing":true,
	// "pagekeys_handled":true,
	// "last_snippet_type":"",
	// "display_real_names_override":0,
	// "time24":false,
	// "enter_is_special_in_tbt":false,
	// "graphic_emoticons":false,
	// "convert_emoticons":true,
	// "autoplay_chat_sounds":true,
	// "ss_emojis":true,
	// "sidebar_behavior":"",
	// "mark_msgs_read_immediately":true,
	// "start_scroll_at_oldest":true,
	// "snippet_editor_wrap_long_lines":false,
	// "ls_disabled":false,
	// "sidebar_theme":"default",
	// "sidebar_theme_custom_values":"",
	// "f_key_search":false,
	// "k_key_omnibox":true,
	// "speak_growls":false,
	// "mac_speak_voice":"com.apple.speech.synthesis.voice.Alex",
	// "mac_speak_speed":250,
	// "comma_key_prefs":false,
	// "at_channel_suppressed_channels":"",
	// "push_at_channel_suppressed_channels":"",
	// "prompted_for_email_disabling":false,
	// "full_text_extracts":false,
	// "no_text_in_notifications":false,
	// "muted_channels":"",
	// "no_macssb1_banner":false,
	// "privacy_policy_seen":true,
	// "search_exclude_bots":false,
	// "fuzzy_matching":false
}

// UserDetails contains user details coming in the initial response from StartRTM
type UserDetails struct {
	ID             string    `json:"id"`
	Name           string    `json:"name"`
	Created        JSONTime  `json:"created"`
	ManualPresence string    `json:"manual_presence"`
	Prefs          UserPrefs `json:"prefs"`
}

// JSONTime exists so that we can have a String method converting the date
type JSONTime int64

// String converts the unix timestamp into a string
func (t JSONTime) String() string {
	tm := t.Time()
	return fmt.Sprintf("\"%s\"", tm.Format("Mon Jan _2"))
}

// Time returns a `time.Time` representation of this value.
func (t JSONTime) Time() time.Time {
	return time.Unix(int64(t), 0)
}

// Team contains details about a team
type Team struct {
	ID     string `json:"id"`
	Name   string `json:"name"`
	Domain string `json:"domain"`
}

// Icons XXX: needs further investigation
type Icons struct {
	Image36 string `json:"image_36,omitempty"`
	Image48 string `json:"image_48,omitempty"`
	Image72 string `json:"image_72,omitempty"`
}

// Info contains various details about Users, Channels, Bots and the authenticated user.
// It is returned by StartRTM or included in the "ConnectedEvent" RTM event.
type Info struct {
	URL      string       `json:"url,omitempty"`
	User     *UserDetails `json:"self,omitempty"`
	Team     *Team        `json:"team,omitempty"`
	Users    []User       `json:"users,omitempty"`
	Channels []Channel    `json:"channels,omitempty"`
	Groups   []Group      `json:"groups,omitempty"`
	Bots     []Bot        `json:"bots,omitempty"`
	IMs      []IM         `json:"ims,omitempty"`
}

type infoResponseFull struct {
	Info
	WebResponse
}

// GetBotByID returns a bot given a bot id
func (info Info) GetBotByID(botID string) *Bot {
	for _, bot := range info.Bots {
		if bot.ID == botID {
			return &bot
		}
	}
	return nil
}

// GetUserByID returns a user given a user id
func (info Info) GetUserByID(userID string) *User {
	for _, user := range info.Users {
		if user.ID == userID {
			return &user
		}
	}
	return nil
}

// GetChannelByID returns a channel given a channel id
func (info Info) GetChannelByID(channelID string) *Channel {
	for _, channel := range info.Channels {
		if channel.ID == channelID {
			return &channel
		}
	}
	return nil
}

// GetGroupByID returns a group given a group id
func (info Info) GetGroupByID(groupID string) *Group {
	for _, group := range info.Groups {
		if group.ID == groupID {
			return &group
		}
	}
	return nil
}

// GetIMByID returns an IM given an IM id
func (info Info) GetIMByID(imID string) *IM {
	for _, im := range info.IMs {
		if im.ID == imID {
			return &im
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package interfaces

import (
	"github.com/4406arthur/clean-micro-article-service/pkg/entity"
)

//TODO
type DbHandler interface {
	GetAll() ([]*map[string]interface{}, error)
	GetByTitle(title string) (map[string]interface{}, error)
	Save(article map[string]interface{}) error
}

type DbRepo struct {
	dbHandler DbHandler
}

type DbArticleRepo DbRepo

func (repo *DbArticleRepo) FetchAll() ([]*entity.Article, error) {
	articles, err := repo.dbHandler.GetAll()
	if err != nil {
		return nil, err
	}

	return articles, nil
}

func (repo *DbArticleRepo) GetByTitle(title string) (*entity.Article, error) {
	article, err := repo.dbHandler.GetByTitle(title)
	if err != nil {
		return nil, err
	}
	return &article, nil
}

func (repo *DbArticleRepo) Store(article entity.Article) error {
	err := repo.dbHandler.Save(article)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>new func<commit_after>package interfaces

import (
	"github.com/4406arthur/clean-micro-article-service/pkg/entity"
)

//TODO
type DbHandler interface {
	GetAll() ([]*map[string]interface{}, error)
	GetByTitle(title string) (map[string]interface{}, error)
	Save(article map[string]interface{}) error
}

type DbRepo struct {
	dbHandler DbHandler
}

type DbArticleRepo DbRepo

func NewDbArticleRepo(dbHandler DbHandler) *DbArticleRepo {
	dbArticleRepo := new(DbArticleRepo)
	dbArticleRepo.dbHandler = dbHandler
	return dbArticleRepo
}

func (repo *DbArticleRepo) FetchAll() ([]*entity.Article, error) {
	articles, err := repo.dbHandler.GetAll()
	if err != nil {
		return nil, err
	}

	return articles, nil
}

func (repo *DbArticleRepo) GetByTitle(title string) (*entity.Article, error) {
	article, err := repo.dbHandler.GetByTitle(title)
	if err != nil {
		return nil, err
	}
	return &article, nil
}

func (repo *DbArticleRepo) Store(article entity.Article) error {
	err := repo.dbHandler.Save(article)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package buildah

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/containers/buildah/pkg/unshare"
	"github.com/containers/storage"
	"github.com/containers/storage/pkg/system"
	"github.com/sirupsen/logrus"
)

// InfoData holds the info type, i.e store, host etc and the data for each type
type InfoData struct {
	Type string
	Data map[string]interface{}
}

// Info returns the store and host information
func Info(store storage.Store) ([]InfoData, error) {
	info := []InfoData{}
	// get host information
	hostInfo := hostInfo()
	info = append(info, InfoData{Type: "host", Data: hostInfo})

	// get store information
	storeInfo, err := storeInfo(store)
	if err != nil {
		logrus.Error(err, "error getting store info")
	}
	info = append(info, InfoData{Type: "store", Data: storeInfo})
	return info, nil
}

func hostInfo() map[string]interface{} {
	info := map[string]interface{}{}
	info["os"] = runtime.GOOS
	info["arch"] = runtime.GOARCH
	info["cpus"] = runtime.NumCPU()
	info["rootless"] = unshare.IsRootless()
	mi, err := system.ReadMemInfo()
	if err != nil {
		logrus.Error(err, "err reading memory info")
		info["MemTotal"] = ""
		info["MenFree"] = ""
		info["SwapTotal"] = ""
		info["SwapFree"] = ""
	} else {
		info["MemTotal"] = mi.MemTotal
		info["MenFree"] = mi.MemFree
		info["SwapTotal"] = mi.SwapTotal
		info["SwapFree"] = mi.SwapFree
	}
	hostDistributionInfo := getHostDistributionInfo()
	info["Distribution"] = map[string]interface{}{
		"distribution": hostDistributionInfo["Distribution"],
		"version":      hostDistributionInfo["Version"],
	}

	kv, err := readKernelVersion()
	if err != nil {
		logrus.Error(err, "error reading kernel version")
	}
	info["kernel"] = kv

	up, err := readUptime()
	if err != nil {
		logrus.Error(err, "error reading up time")
	}
	// Convert uptime in seconds to a human-readable format
	upSeconds := up + "s"
	upDuration, err := time.ParseDuration(upSeconds)
	if err != nil {
		logrus.Error(err, "error parsing system uptime")
	}

	hoursFound := false
	var timeBuffer bytes.Buffer
	var hoursBuffer bytes.Buffer
	for _, elem := range upDuration.String() {
		timeBuffer.WriteRune(elem)
		if elem == 'h' || elem == 'm' {
			timeBuffer.WriteRune(' ')
			if elem == 'h' {
				hoursFound = true
			}
		}
		if !hoursFound {
			hoursBuffer.WriteRune(elem)
		}
	}

	info["uptime"] = timeBuffer.String()
	if hoursFound {
		hours, err := strconv.ParseFloat(hoursBuffer.String(), 64)
		if err == nil {
			days := hours / 24
			info["uptime"] = fmt.Sprintf("%s (Approximately %.2f days)", info["uptime"], days)
		}
	}

	host, err := os.Hostname()
	if err != nil {
		logrus.Error(err, "error getting hostname")
	}
	info["hostname"] = host

	return info
}

// top-level "store" info
func storeInfo(store storage.Store) (map[string]interface{}, error) {
	// lets say storage driver in use, number of images, number of containers
	info := map[string]interface{}{}
	info["GraphRoot"] = store.GraphRoot()
	info["RunRoot"] = store.RunRoot()
	info["GraphDriverName"] = store.GraphDriverName()
	info["GraphOptions"] = store.GraphOptions()
	statusPairs, err := store.Status()
	if err != nil {
		return nil, err
	}
	status := map[string]string{}
	for _, pair := range statusPairs {
		status[pair[0]] = pair[1]
	}
	info["GraphStatus"] = status
	images, err := store.Images()
	if err != nil {
		logrus.Error(err, "error getting number of images")
	}
	info["ImageStore"] = map[string]interface{}{
		"number": len(images),
	}

	containers, err := store.Containers()
	if err != nil {
		logrus.Error(err, "error getting number of containers")
	}
	info["ContainerStore"] = map[string]interface{}{
		"number": len(containers),
	}

	return info, nil
}

func readKernelVersion() (string, error) {
	buf, err := ioutil.ReadFile("/proc/version")
	if err != nil {
		return "", err
	}
	f := bytes.Fields(buf)
	if len(f) < 2 {
		return string(bytes.TrimSpace(buf)), nil
	}
	return string(f[2]), nil
}

func readUptime() (string, error) {
	buf, err := ioutil.ReadFile("/proc/uptime")
	if err != nil {
		return "", err
	}
	f := bytes.Fields(buf)
	if len(f) < 1 {
		return "", fmt.Errorf("invalid uptime")
	}
	return string(f[0]), nil
}

// getHostDistributionInfo returns a map containing the host's distribution and version
func getHostDistributionInfo() map[string]string {
	dist := make(map[string]string)

	// Populate values in case we cannot find the values
	// or the file
	dist["Distribution"] = "unknown"
	dist["Version"] = "unknown"

	f, err := os.Open("/etc/os-release")
	if err != nil {
		return dist
	}
	defer f.Close()

	l := bufio.NewScanner(f)
	for l.Scan() {
		if strings.HasPrefix(l.Text(), "ID=") {
			dist["Distribution"] = strings.TrimPrefix(l.Text(), "ID=")
		}
		if strings.HasPrefix(l.Text(), "VERSION_ID=") {
			dist["Version"] = strings.Trim(strings.TrimPrefix(l.Text(), "VERSION_ID="), "\"")
		}
	}
	return dist
}
<commit_msg>info: add cgroups2<commit_after>package buildah

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/containers/buildah/pkg/cgroups"
	"github.com/containers/buildah/pkg/unshare"
	"github.com/containers/storage"
	"github.com/containers/storage/pkg/system"
	"github.com/sirupsen/logrus"
)

// InfoData holds the info type, i.e store, host etc and the data for each type
type InfoData struct {
	Type string
	Data map[string]interface{}
}

// Info returns the store and host information
func Info(store storage.Store) ([]InfoData, error) {
	info := []InfoData{}
	// get host information
	hostInfo := hostInfo()
	info = append(info, InfoData{Type: "host", Data: hostInfo})

	// get store information
	storeInfo, err := storeInfo(store)
	if err != nil {
		logrus.Error(err, "error getting store info")
	}
	info = append(info, InfoData{Type: "store", Data: storeInfo})
	return info, nil
}

func hostInfo() map[string]interface{} {
	info := map[string]interface{}{}
	info["os"] = runtime.GOOS
	info["arch"] = runtime.GOARCH
	info["cpus"] = runtime.NumCPU()
	info["rootless"] = unshare.IsRootless()

	unified, err := cgroups.IsCgroup2UnifiedMode()
	if err != nil {
		logrus.Error(err, "err reading cgroups mode")
	}
	cgroupVersion := "v1"
	if unified {
		cgroupVersion = "v2"
	}
	info["CgroupVersion"] = cgroupVersion

	mi, err := system.ReadMemInfo()
	if err != nil {
		logrus.Error(err, "err reading memory info")
		info["MemTotal"] = ""
		info["MenFree"] = ""
		info["SwapTotal"] = ""
		info["SwapFree"] = ""
	} else {
		info["MemTotal"] = mi.MemTotal
		info["MenFree"] = mi.MemFree
		info["SwapTotal"] = mi.SwapTotal
		info["SwapFree"] = mi.SwapFree
	}
	hostDistributionInfo := getHostDistributionInfo()
	info["Distribution"] = map[string]interface{}{
		"distribution": hostDistributionInfo["Distribution"],
		"version":      hostDistributionInfo["Version"],
	}

	kv, err := readKernelVersion()
	if err != nil {
		logrus.Error(err, "error reading kernel version")
	}
	info["kernel"] = kv

	up, err := readUptime()
	if err != nil {
		logrus.Error(err, "error reading up time")
	}
	// Convert uptime in seconds to a human-readable format
	upSeconds := up + "s"
	upDuration, err := time.ParseDuration(upSeconds)
	if err != nil {
		logrus.Error(err, "error parsing system uptime")
	}

	hoursFound := false
	var timeBuffer bytes.Buffer
	var hoursBuffer bytes.Buffer
	for _, elem := range upDuration.String() {
		timeBuffer.WriteRune(elem)
		if elem == 'h' || elem == 'm' {
			timeBuffer.WriteRune(' ')
			if elem == 'h' {
				hoursFound = true
			}
		}
		if !hoursFound {
			hoursBuffer.WriteRune(elem)
		}
	}

	info["uptime"] = timeBuffer.String()
	if hoursFound {
		hours, err := strconv.ParseFloat(hoursBuffer.String(), 64)
		if err == nil {
			days := hours / 24
			info["uptime"] = fmt.Sprintf("%s (Approximately %.2f days)", info["uptime"], days)
		}
	}

	host, err := os.Hostname()
	if err != nil {
		logrus.Error(err, "error getting hostname")
	}
	info["hostname"] = host

	return info
}

// top-level "store" info
func storeInfo(store storage.Store) (map[string]interface{}, error) {
	// lets say storage driver in use, number of images, number of containers
	info := map[string]interface{}{}
	info["GraphRoot"] = store.GraphRoot()
	info["RunRoot"] = store.RunRoot()
	info["GraphDriverName"] = store.GraphDriverName()
	info["GraphOptions"] = store.GraphOptions()
	statusPairs, err := store.Status()
	if err != nil {
		return nil, err
	}
	status := map[string]string{}
	for _, pair := range statusPairs {
		status[pair[0]] = pair[1]
	}
	info["GraphStatus"] = status
	images, err := store.Images()
	if err != nil {
		logrus.Error(err, "error getting number of images")
	}
	info["ImageStore"] = map[string]interface{}{
		"number": len(images),
	}

	containers, err := store.Containers()
	if err != nil {
		logrus.Error(err, "error getting number of containers")
	}
	info["ContainerStore"] = map[string]interface{}{
		"number": len(containers),
	}

	return info, nil
}

func readKernelVersion() (string, error) {
	buf, err := ioutil.ReadFile("/proc/version")
	if err != nil {
		return "", err
	}
	f := bytes.Fields(buf)
	if len(f) < 2 {
		return string(bytes.TrimSpace(buf)), nil
	}
	return string(f[2]), nil
}

func readUptime() (string, error) {
	buf, err := ioutil.ReadFile("/proc/uptime")
	if err != nil {
		return "", err
	}
	f := bytes.Fields(buf)
	if len(f) < 1 {
		return "", fmt.Errorf("invalid uptime")
	}
	return string(f[0]), nil
}

// getHostDistributionInfo returns a map containing the host's distribution and version
func getHostDistributionInfo() map[string]string {
	dist := make(map[string]string)

	// Populate values in case we cannot find the values
	// or the file
	dist["Distribution"] = "unknown"
	dist["Version"] = "unknown"

	f, err := os.Open("/etc/os-release")
	if err != nil {
		return dist
	}
	defer f.Close()

	l := bufio.NewScanner(f)
	for l.Scan() {
		if strings.HasPrefix(l.Text(), "ID=") {
			dist["Distribution"] = strings.TrimPrefix(l.Text(), "ID=")
		}
		if strings.HasPrefix(l.Text(), "VERSION_ID=") {
			dist["Version"] = strings.Trim(strings.TrimPrefix(l.Text(), "VERSION_ID="), "\"")
		}
	}
	return dist
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/juju/cmd"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/cmd/envcmd"
	"github.com/juju/juju/constraints"
	"github.com/juju/juju/juju"
)

type EnsureAvailabilityCommand struct {
	envcmd.EnvCommandBase
	out cmd.Output

	NumStateServers int
	// If specified, use this series for newly created machines,
	// else use the environment's default-series
	Series string
	// If specified, these constraints will be merged with those
	// already in the environment when creating new machines.
	Constraints constraints.Value
}

const ensureAvailabilityDoc = `
To ensure availability of deployed services, the Juju infrastructure
must itself be highly available.  Ensure-availability must be called
to ensure that the specified number of state servers are made available.

An odd number of state servers is required.

Examples:
 juju ensure-availability
     Ensure that the system is still in highly available mode. If
     there is only 1 state server running, this will ensure there
     are 3 running. If you have previously requested more than 3,
     then that number will be ensured.
 juju ensure-availability -n 5 --series=trusty
     Ensure that 5 state servers are available, with newly created
     state server machines having the "trusty" series.
 juju ensure-availability -n 7 --constraints mem=8G
     Ensure that 7 state servers are available, with newly created
     state server machines having the default series, and at least
     8GB RAM.
`

// FormatYaml marshals value to a yaml-formatted []byte, unless value is nil.
func formatSimple(value interface{}) ([]byte, error) {
	ensureAvailabilityResult, ok := value.(formattedResult)
	if !ok {
		return nil, nil
	}

	buff := &bytes.Buffer{}

	if len(ensureAvailabilityResult.Maintained) > 0 {
		_, err := fmt.Fprintf(buff, "maintaining machines: %s\n", machineList(ensureAvailabilityResult.Maintained))
		if err != nil {
			return nil, err
		}
	}
	if len(ensureAvailabilityResult.Added) > 0 {
		_, err := fmt.Fprintf(buff, "adding machines: %s\n", machineList(ensureAvailabilityResult.Added))
		if err != nil {
			return nil, err
		}
	}
	if len(ensureAvailabilityResult.Removed) > 0 {
		_, err := fmt.Fprintf(buff, "removing machines: %s\n", machineList(ensureAvailabilityResult.Removed))
		if err != nil {
			return nil, err
		}
	}
	return buff.Bytes(), nil
}

func (c *EnsureAvailabilityCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "ensure-availability",
		Purpose: "ensure the availability of Juju state servers",
		Doc:     ensureAvailabilityDoc,
	}
}

func (c *EnsureAvailabilityCommand) SetFlags(f *gnuflag.FlagSet) {
	f.IntVar(&c.NumStateServers, "n", 0, "number of state servers to make available")
	f.StringVar(&c.Series, "series", "", "the charm series")
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "additional machine constraints")
	c.out.AddFlags(f, "simple", map[string]cmd.Formatter{
		"yaml":   cmd.FormatYaml,
		"json":   cmd.FormatJson,
		"simple": formatSimple,
	})

}

func (c *EnsureAvailabilityCommand) Init(args []string) error {
	if c.NumStateServers < 0 || (c.NumStateServers%2 != 1 && c.NumStateServers != 0) {
		return fmt.Errorf("must specify a number of state servers odd and non-negative")
	}
	return cmd.CheckEmpty(args)
}

type formattedResult struct {
	Maintained []string `json:"maintained,omitempty" yaml:",flow,omitempty"`
	Removed    []string `json:"removed,omitempty" yaml:",flow,omitempty"`
	Added      []string `json:"added,omitempty" yaml:",flow,omitempty"`
}

// Run connects to the environment specified on the command line
// and calls EnsureAvailability.
func (c *EnsureAvailabilityCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()
	ensureAvailabilityResult, err := client.EnsureAvailability(c.NumStateServers, c.Constraints, c.Series)
	if err != nil {
		return err
	}

	result := formattedResult{
		Added:      ensureAvailabilityResult.Added,
		Removed:    ensureAvailabilityResult.Removed,
		Maintained: ensureAvailabilityResult.Maintained,
	}
	return c.out.Write(ctx, result)
}

// Format machine list for presentation.
func machineList(machines []string) string {
	quoted := []string{}
	for _, machineId := range machines {
		quoted = append(quoted, fmt.Sprintf("%q", machineId))
	}
	return strings.Join(quoted, ", ")
}
<commit_msg>Report result type error.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/juju/cmd"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/cmd/envcmd"
	"github.com/juju/juju/constraints"
	"github.com/juju/juju/juju"
)

type EnsureAvailabilityCommand struct {
	envcmd.EnvCommandBase
	out cmd.Output

	NumStateServers int
	// If specified, use this series for newly created machines,
	// else use the environment's default-series
	Series string
	// If specified, these constraints will be merged with those
	// already in the environment when creating new machines.
	Constraints constraints.Value
}

const ensureAvailabilityDoc = `
To ensure availability of deployed services, the Juju infrastructure
must itself be highly available.  Ensure-availability must be called
to ensure that the specified number of state servers are made available.

An odd number of state servers is required.

Examples:
 juju ensure-availability
     Ensure that the system is still in highly available mode. If
     there is only 1 state server running, this will ensure there
     are 3 running. If you have previously requested more than 3,
     then that number will be ensured.
 juju ensure-availability -n 5 --series=trusty
     Ensure that 5 state servers are available, with newly created
     state server machines having the "trusty" series.
 juju ensure-availability -n 7 --constraints mem=8G
     Ensure that 7 state servers are available, with newly created
     state server machines having the default series, and at least
     8GB RAM.
`

// FormatYaml marshals value to a yaml-formatted []byte, unless value is nil.
func formatSimple(value interface{}) ([]byte, error) {
	ensureAvailabilityResult, ok := value.(formattedResult)
	if !ok {
		return nil, fmt.Errorf("unexpected result type for ensure-availability call")
	}

	buff := &bytes.Buffer{}

	if len(ensureAvailabilityResult.Maintained) > 0 {
		_, err := fmt.Fprintf(buff, "maintaining machines: %s\n", machineList(ensureAvailabilityResult.Maintained))
		if err != nil {
			return nil, err
		}
	}
	if len(ensureAvailabilityResult.Added) > 0 {
		_, err := fmt.Fprintf(buff, "adding machines: %s\n", machineList(ensureAvailabilityResult.Added))
		if err != nil {
			return nil, err
		}
	}
	if len(ensureAvailabilityResult.Removed) > 0 {
		_, err := fmt.Fprintf(buff, "removing machines: %s\n", machineList(ensureAvailabilityResult.Removed))
		if err != nil {
			return nil, err
		}
	}
	return buff.Bytes(), nil
}

func (c *EnsureAvailabilityCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "ensure-availability",
		Purpose: "ensure the availability of Juju state servers",
		Doc:     ensureAvailabilityDoc,
	}
}

func (c *EnsureAvailabilityCommand) SetFlags(f *gnuflag.FlagSet) {
	f.IntVar(&c.NumStateServers, "n", 0, "number of state servers to make available")
	f.StringVar(&c.Series, "series", "", "the charm series")
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "additional machine constraints")
	c.out.AddFlags(f, "simple", map[string]cmd.Formatter{
		"yaml":   cmd.FormatYaml,
		"json":   cmd.FormatJson,
		"simple": formatSimple,
	})

}

func (c *EnsureAvailabilityCommand) Init(args []string) error {
	if c.NumStateServers < 0 || (c.NumStateServers%2 != 1 && c.NumStateServers != 0) {
		return fmt.Errorf("must specify a number of state servers odd and non-negative")
	}
	return cmd.CheckEmpty(args)
}

type formattedResult struct {
	Maintained []string `json:"maintained,omitempty" yaml:",flow,omitempty"`
	Removed    []string `json:"removed,omitempty" yaml:",flow,omitempty"`
	Added      []string `json:"added,omitempty" yaml:",flow,omitempty"`
}

// Run connects to the environment specified on the command line
// and calls EnsureAvailability.
func (c *EnsureAvailabilityCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()
	ensureAvailabilityResult, err := client.EnsureAvailability(c.NumStateServers, c.Constraints, c.Series)
	if err != nil {
		return err
	}

	result := formattedResult{
		Added:      ensureAvailabilityResult.Added,
		Removed:    ensureAvailabilityResult.Removed,
		Maintained: ensureAvailabilityResult.Maintained,
	}
	return c.out.Write(ctx, result)
}

// Format machine list for presentation.
func machineList(machines []string) string {
	quoted := []string{}
	for _, machineId := range machines {
		quoted = append(quoted, fmt.Sprintf("%q", machineId))
	}
	return strings.Join(quoted, ", ")
}
<|endoftext|>


<commit_before>// +build windows

package i18n_test

import (
	"os"
	"path/filepath"

	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	"github.com/cloudfoundry/cli/cf/i18n"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	"github.com/pivotal-cf-experimental/jibber_jabber"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("i18n.Init() function", func() {
	var (
		configRepo core_config.ReadWriter
	)

	BeforeEach(func() {
		i18n.Resources_path = filepath.Join("cf", "i18n", "test_fixtures")
		configRepo = testconfig.NewRepositoryWithDefaults()
	})

	Describe("When a user has a locale configuration set", func() {
		Context("creates a valid T function", func() {
			BeforeEach(func() {
				configRepo.SetLocale("en_US")
			})

			It("returns a usable T function for simple strings", func() {
				T := i18n.Init(configRepo)
				Ω(T).ShouldNot(BeNil())

				translation := T("Hello world!")
				Ω("Hello world!").Should(Equal(translation))
			})

			It("returns a usable T function for complex strings (interpolated)", func() {
				T := i18n.Init(configRepo)
				Ω(T).ShouldNot(BeNil())

				translation := T("Deleting domain {{.DomainName}} as {{.Username}}...", map[string]interface{}{"DomainName": "foo.com", "Username": "Anand"})
				Ω("Deleting domain foo.com as Anand...").Should(Equal(translation))
			})
		})
	})

	Describe("When a user does not have a locale configuration set", func() {
		BeforeEach(func() {
			//All these tests require the system language to be English
			Ω(jibber_jabber.DetectIETF()).Should(Equal("en-US"))
		})

		Context("creates a valid T function", func() {
			BeforeEach(func() {
				os.Setenv("LC_ALL", "en_US.UTF-8")
			})

			It("returns a usable T function for simple strings", func() {
				T := i18n.Init(configRepo)
				Ω(T).ShouldNot(BeNil())

				translation := T("Change user password")
				Ω("Change user password").Should(Equal(translation))
			})

			It("returns a usable T function for complex strings (interpolated)", func() {
				T := i18n.Init(configRepo)
				Ω(T).ShouldNot(BeNil())

				translation := T("Deleting domain {{.DomainName}} as {{.Username}}...", map[string]interface{}{"DomainName": "foo", "Username": "Anand"})
				Ω("Deleting domain foo as Anand...").Should(Equal(translation))
			})
		})

		It("matches zh_CN to zh_Hans", func() {
			os.Setenv("LC_ALL", "zh_CN.UTF-8")
			T := i18n.Init(configRepo)
			Ω(T).ShouldNot(BeNil())

			translation := T("No buildpacks found")
			Ω("buildpack未找到").Should(Equal(translation))
		})

		It("matches zh_TW to zh_Hant", func() {
			os.Setenv("LC_ALL", "zh_TW.UTF-8")
			T := i18n.Init(configRepo)
			Ω(T).ShouldNot(BeNil())

			translation := T("No buildpacks found")
			Ω("(Hant)No buildpacks found").Should(Equal(translation))
		})

		It("matches zh_HK to zh_Hant", func() {
			os.Setenv("LC_ALL", "zh_HK.UTF-8")
			T := i18n.Init(configRepo)
			Ω(T).ShouldNot(BeNil())

			translation := T("No buildpacks found")
			Ω("(Hant)No buildpacks found").Should(Equal(translation))
		})
	})
})
<commit_msg>Revert "Revert "fix failing HK/TW Windows 32 unit test""<commit_after>// +build windows

package i18n_test

import (
	"os"
	"path/filepath"

	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	"github.com/cloudfoundry/cli/cf/i18n"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	"github.com/pivotal-cf-experimental/jibber_jabber"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("i18n.Init() function", func() {
	var (
		configRepo core_config.ReadWriter
	)

	BeforeEach(func() {
		i18n.Resources_path = filepath.Join("cf", "i18n", "test_fixtures")
		configRepo = testconfig.NewRepositoryWithDefaults()
	})

	Describe("When a user has a locale configuration set", func() {
		Context("creates a valid T function", func() {
			BeforeEach(func() {
				configRepo.SetLocale("en_US")
			})

			It("returns a usable T function for simple strings", func() {
				T := i18n.Init(configRepo)
				Ω(T).ShouldNot(BeNil())

				translation := T("Hello world!")
				Ω("Hello world!").Should(Equal(translation))
			})

			It("returns a usable T function for complex strings (interpolated)", func() {
				T := i18n.Init(configRepo)
				Ω(T).ShouldNot(BeNil())

				translation := T("Deleting domain {{.DomainName}} as {{.Username}}...", map[string]interface{}{"DomainName": "foo.com", "Username": "Anand"})
				Ω("Deleting domain foo.com as Anand...").Should(Equal(translation))
			})
		})
	})

	Describe("When a user does not have a locale configuration set", func() {
		BeforeEach(func() {
			os.Setenv("LC_ALL", "en_US.UTF-8")

			//All these tests require the system language to be English
			Ω(jibber_jabber.DetectIETF()).Should(Equal("en-US"))
		})

		Context("creates a valid T function", func() {
			It("returns a usable T function for simple strings", func() {
				T := i18n.Init(configRepo)
				Ω(T).ShouldNot(BeNil())

				translation := T("Change user password")
				Ω("Change user password").Should(Equal(translation))
			})

			It("returns a usable T function for complex strings (interpolated)", func() {
				T := i18n.Init(configRepo)
				Ω(T).ShouldNot(BeNil())

				translation := T("Deleting domain {{.DomainName}} as {{.Username}}...", map[string]interface{}{"DomainName": "foo", "Username": "Anand"})
				Ω("Deleting domain foo as Anand...").Should(Equal(translation))
			})
		})

	})

	Describe("When locale is HK/TW", func() {
		It("matches zh_CN to zh_Hans", func() {
			os.Setenv("LC_ALL", "zh_CN.UTF-8")
			T := i18n.Init(configRepo)
			Ω(T).ShouldNot(BeNil())

			translation := T("No buildpacks found")
			Ω("buildpack未找到").Should(Equal(translation))
		})

		It("matches zh_TW to zh_Hant", func() {
			os.Setenv("LC_ALL", "zh_TW.UTF-8")
			T := i18n.Init(configRepo)
			Ω(T).ShouldNot(BeNil())

			translation := T("No buildpacks found")
			Ω("(Hant)No buildpacks found").Should(Equal(translation))
		})

		It("matches zh_HK to zh_Hant", func() {
			os.Setenv("LC_ALL", "zh_HK.UTF-8")
			T := i18n.Init(configRepo)
			Ω(T).ShouldNot(BeNil())

			translation := T("No buildpacks found")
			Ω("(Hant)No buildpacks found").Should(Equal(translation))
		})
	})
})
<|endoftext|>


<commit_before>package cachet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
	"time"
)

const (
	// testCachetInstance is a test instance url that won`t be called
	testCachetInstance = "https://demo.cachethq.io/"
)

var (
	// testMux is the HTTP request multiplexer used with the test server.
	testMux *http.ServeMux

	// testClient is the cachet client being tested.
	testClient *Client

	// testServer is a test HTTP server used to provide mock API responses.
	testServer *httptest.Server
)

//type testValues map[string]string

// setup sets up a test HTTP server along with a cachet.Client that is configured to talk to that test server.
// Tests should register handlers on mux which provide mock responses for the API method being tested.
func setup() {
	// Test server
	testMux = http.NewServeMux()
	testServer = httptest.NewServer(testMux)

	// Cachet client configured to use test server
	testClient, _ = NewClient(testServer.URL, nil)
}

// teardown closes the test HTTP server.
func teardown() {
	testServer.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if got := r.Method; got != want {
		t.Errorf("Request method: %v, want %v", got, want)
	}
}

func TestNewClient_NoCachetInstance(t *testing.T) {
	mockData := []string{"", "://not-existing"}
	for _, data := range mockData {
		c, err := NewClient(data, nil)
		if c != nil {
			t.Errorf("NewClient return is not nil. Expected no client. Got %+v", c)
		}
		if err == nil {
			t.Error("No error occured by empty Cachet Instance. Expected one.")
		}
	}
}

func TestNewClient_HttpClient(t *testing.T) {
	customHTTPClient := &http.Client{
		Timeout: 30 * time.Second,
	}
	mockData := []struct {
		HTTPClient         *http.Client
		ExpectedHTTPClient *http.Client
	}{
		{nil, http.DefaultClient},
		{customHTTPClient, customHTTPClient},
	}

	for _, mock := range mockData {
		c, err := NewClient(testCachetInstance, mock.HTTPClient)
		if err != nil {
			t.Errorf("An error occured. Expected nil. Got %+v.", err)
		}
		if reflect.DeepEqual(c.client, mock.ExpectedHTTPClient) == false {
			t.Errorf("Wrong HTTP Client. Expected %+v. Got %+v", mock.ExpectedHTTPClient, c.client)
		}
	}
}

func TestNewClient_Services(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	if c.Authentication == nil {
		t.Error("No AuthenticationService found.")
	}
	if c.General == nil {
		t.Error("No GeneralService found.")
	}
	if c.Components == nil {
		t.Error("No ComponentsService found.")
	}
	if c.Incidents == nil {
		t.Error("No IncidentsService found.")
	}
	if c.Metrics == nil {
		t.Error("No MetricsService found.")
	}
	if c.Subscribers == nil {
		t.Error("No SubscribersService found.")
	}

}

func TestNewRequest(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	inURL, outURL := "/foo", testCachetInstance+"foo"
	inBody, outBody := &Component{Name: "Go API (by Token)", Status: 1}, `{"name":"Go API (by Token)","status":1}`+"\n"
	req, _ := c.NewRequest("POST", inURL, inBody)

	// Test that relative URL was expanded
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want)
	}

	// Test that body was JSON encoded
	body, _ := ioutil.ReadAll(req.Body)
	if got, want := string(body), outBody; got != want {
		t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want)
	}
}

func TestNewRequest_InvalidJSON(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	type T struct {
		A map[int]interface{}
	}
	_, err = c.NewRequest("GET", "/", &T{})

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*json.UnsupportedTypeError); !ok {
		t.Errorf("Expected a JSON error; got %+v.", err)
	}
}

func TestNewRequest_BadURL(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	_, err = c.NewRequest("GET", ":", nil)
	if err == nil {
		t.Errorf("Expected error to be returned")
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected URL parse error, got %+v", err)
	}
}

// If a nil body is passed to cachet.NewRequest, make sure that nil is also passed to http.NewRequest.
// In most cases, passing an io.Reader that returns no content is fine,
// since there is no difference between an HTTP request body that is an empty string versus one that is not set at all.
// However in certain cases, intermediate systems may treat these differently resulting in subtle errors.
func TestNewRequest_EmptyBody(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	req, err := c.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("NewRequest returned unexpected error: %+v", err)
	}
	if req.Body != nil {
		t.Fatalf("Constructed request contains a non-nil Body")
	}
}

func TestDo(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		A string
	}

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"A":"a"}`)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	body := new(foo)
	testClient.Do(req, body)

	want := &foo{"a"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Response body = %v, want %v", body, want)
	}
}

func TestDo_ioWriter(t *testing.T) {
	setup()
	defer teardown()
	content := `{"A":"a"}`

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, content)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	var buf []byte
	actual := bytes.NewBuffer(buf)
	testClient.Do(req, actual)

	expected := []byte(content)
	if !reflect.DeepEqual(actual.Bytes(), expected) {
		t.Errorf("Response body = %v, want %v", actual, string(expected))
	}
}

func TestDo_HTTPError(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Bad Request", 400)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

// Test handling of an error caused by the internal http client's Do() function.
// A redirect loop is pretty unlikely to occur within the Cacheterrit API, but does allow us to exercise the right code path.
func TestDo_RedirectLoop(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected a URL error; got %#v.", err)
	}
}
<commit_msg>Fixed unit tests<commit_after>package cachet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
	"time"
)

const (
	// testCachetInstance is a test instance url that won`t be called
	testCachetInstance = "https://demo.cachethq.io/"
)

var (
	// testMux is the HTTP request multiplexer used with the test server.
	testMux *http.ServeMux

	// testClient is the cachet client being tested.
	testClient *Client

	// testServer is a test HTTP server used to provide mock API responses.
	testServer *httptest.Server
)

//type testValues map[string]string

// setup sets up a test HTTP server along with a cachet.Client that is configured to talk to that test server.
// Tests should register handlers on mux which provide mock responses for the API method being tested.
func setup() {
	// Test server
	testMux = http.NewServeMux()
	testServer = httptest.NewServer(testMux)

	// Cachet client configured to use test server
	testClient, _ = NewClient(testServer.URL, nil)
}

// teardown closes the test HTTP server.
func teardown() {
	testServer.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if got := r.Method; got != want {
		t.Errorf("Request method: %v, want %v", got, want)
	}
}

func TestNewClient_NoCachetInstance(t *testing.T) {
	mockData := []string{"", "://not-existing"}
	for _, data := range mockData {
		c, err := NewClient(data, nil)
		if c != nil {
			t.Errorf("NewClient return is not nil. Expected no client. Got %+v", c)
		}
		if err == nil {
			t.Error("No error occured by empty Cachet Instance. Expected one.")
		}
	}
}

func TestNewClient_HttpClient(t *testing.T) {
	customHTTPClient := &http.Client{
		Timeout: 30 * time.Second,
	}
	mockData := []struct {
		HTTPClient         *http.Client
		ExpectedHTTPClient *http.Client
	}{
		{nil, http.DefaultClient},
		{customHTTPClient, customHTTPClient},
	}

	for _, mock := range mockData {
		c, err := NewClient(testCachetInstance, mock.HTTPClient)
		if err != nil {
			t.Errorf("An error occured. Expected nil. Got %+v.", err)
		}
		if reflect.DeepEqual(c.client, mock.ExpectedHTTPClient) == false {
			t.Errorf("Wrong HTTP Client. Expected %+v. Got %+v", mock.ExpectedHTTPClient, c.client)
		}
	}
}

func TestNewClient_Services(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	if c.Authentication == nil {
		t.Error("No AuthenticationService found.")
	}
	if c.General == nil {
		t.Error("No GeneralService found.")
	}
	if c.Components == nil {
		t.Error("No ComponentsService found.")
	}
	if c.Incidents == nil {
		t.Error("No IncidentsService found.")
	}
	if c.Metrics == nil {
		t.Error("No MetricsService found.")
	}
	if c.Subscribers == nil {
		t.Error("No SubscribersService found.")
	}

}

func TestNewRequest(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	inURL, outURL := "/foo", testCachetInstance+"foo"
	inBody, outBody := &Component{Name: "Go API (by Token)", Status: 1}, `{"name":"Go API (by Token)","status":1,"enabled":false}`+"\n"
	req, _ := c.NewRequest("POST", inURL, inBody)

	// Test that relative URL was expanded
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want)
	}

	// Test that body was JSON encoded
	body, _ := ioutil.ReadAll(req.Body)
	if got, want := string(body), outBody; got != want {
		t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want)
	}
}

func TestNewRequest_InvalidJSON(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	type T struct {
		A map[int]interface{}
	}
	_, err = c.NewRequest("GET", "/", &T{})

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*json.UnsupportedTypeError); !ok {
		t.Errorf("Expected a JSON error; got %+v.", err)
	}
}

func TestNewRequest_BadURL(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	_, err = c.NewRequest("GET", ":", nil)
	if err == nil {
		t.Errorf("Expected error to be returned")
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected URL parse error, got %+v", err)
	}
}

// If a nil body is passed to cachet.NewRequest, make sure that nil is also passed to http.NewRequest.
// In most cases, passing an io.Reader that returns no content is fine,
// since there is no difference between an HTTP request body that is an empty string versus one that is not set at all.
// However in certain cases, intermediate systems may treat these differently resulting in subtle errors.
func TestNewRequest_EmptyBody(t *testing.T) {
	c, err := NewClient(testCachetInstance, nil)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	req, err := c.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("NewRequest returned unexpected error: %+v", err)
	}
	if req.Body != nil {
		t.Fatalf("Constructed request contains a non-nil Body")
	}
}

func TestDo(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		A string
	}

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"A":"a"}`)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	body := new(foo)
	testClient.Do(req, body)

	want := &foo{"a"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Response body = %v, want %v", body, want)
	}
}

func TestDo_ioWriter(t *testing.T) {
	setup()
	defer teardown()
	content := `{"A":"a"}`

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, content)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	var buf []byte
	actual := bytes.NewBuffer(buf)
	testClient.Do(req, actual)

	expected := []byte(content)
	if !reflect.DeepEqual(actual.Bytes(), expected) {
		t.Errorf("Response body = %v, want %v", actual, string(expected))
	}
}

func TestDo_HTTPError(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Bad Request", 400)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

// Test handling of an error caused by the internal http client's Do() function.
// A redirect loop is pretty unlikely to occur within the Cacheterrit API, but does allow us to exercise the right code path.
func TestDo_RedirectLoop(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected a URL error; got %#v.", err)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package components

import (
	"github.com/thethingsnetwork/core"
	//"github.com/thethingsnetwork/core/lorawan"
	"fmt"
	"github.com/thethingsnetwork/core/semtech"
)

var ErrImpossibleConversion = fmt.Errorf("The given packet can't be converted")

func ConvertSemtechPacket(p semtech.Packet) (core.Packet, error) {
	return core.Packet{}, nil
}
<commit_msg>[broker] Change signature of convert method<commit_after>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package components

import (
	"github.com/thethingsnetwork/core"
	//"github.com/thethingsnetwork/core/lorawan"
	"fmt"
	"github.com/thethingsnetwork/core/semtech"
)

var ErrImpossibleConversion = fmt.Errorf("The given packet can't be converted")

func ConvertRXPK(p semtech.RXPK) (core.Packet, error) {
	return core.Packet{}, nil
}
<|endoftext|>


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

package nvim

import (
	"fmt"
	"strings"

	"github.com/garyburd/neovim-go/vim"
)

var (
	errorColor = "Identifier"
	progress   = "Identifier"
	success    = "Function"
)

// Echo provide the vim 'echo' command.
func Echo(v *vim.Vim, format string, a ...interface{}) error {
	return v.Command("echo '" + fmt.Sprintf(format, a...) + "'")
}

// EchoRaw provide the raw output vim 'echo' command.
func EchoRaw(v *vim.Vim, a string) error {
	return v.Command("echo \"" + a + "\"")
}

// Echomsg provide the vim 'echomsg' command.
func Echomsg(v *vim.Vim, a ...interface{}) error {
	return v.Command("echomsg '" + strings.TrimSpace(fmt.Sprintln(a...)) + "'")
}

// Echoerr provide the vim 'echoerr' command.
func Echoerr(v *vim.Vim, format string, a ...interface{}) error {
	return v.Command("echoerr '" + fmt.Sprintf(format, a...) + "'")
}

// EchohlErr provide the vim 'echo' command with the 'echohl' highlighting prefix text.
func EchohlErr(v *vim.Vim, prefix string, a ...interface{}) error {
	v.Command("redraw")
	if prefix != "" {
		prefix += ": "
	}
	text := strings.TrimSpace(fmt.Sprintln(a...))
	return v.Command("echo '" + prefix + "' | echohl " + errorColor + " | echon '" + text + "' | echohl None")
}

// EchohlBefore provide the vim 'echo' command with the 'echohl' highlighting prefix text.
func EchohlBefore(v *vim.Vim, prefix string, highlight string, format string, a ...interface{}) error {
	v.Command("redraw")
	suffix := "' | echohl None | echon '"
	if prefix != "" {
		suffix += ": "
	}
	return v.Command("echohl " + highlight + " | echo '" + prefix + suffix + fmt.Sprintf(format, a...) + "'")
}

// EchohlAfter provide the vim 'echo' command with the 'echohl' highlighting message text.
func EchohlAfter(v *vim.Vim, prefix string, highlight string, format string, a ...interface{}) error {
	v.Command("redraw")
	if prefix != "" {
		prefix += ": "
	}
	return v.Command("echo '" + prefix + "' | echohl " + highlight + " | echon '" + fmt.Sprintf(format, a...) + "' | echohl None")
}

// EchoProgress displays a command progress message to echo area.
func EchoProgress(v *vim.Vim, prefix, before, from, to string) error {
	v.Command("redraw")
	if prefix != "" {
		prefix += ": "
	}
	// TODO(zchee): Refactoring because line too long.
	return v.Command(fmt.Sprintf("echon '%s%s ' | echohl %s | echon '%s' | echohl None | echon ' to ' | echohl %s | echon '%s' | echohl None | echon ' ...'", prefix, before, progress, from, progress, to))
}

// EchoSuccess displays the success of the command to echo area.
func EchoSuccess(v *vim.Vim, prefix string, msg string) error {
	v.Command("redraw")
	if msg != "" {
		msg = " " + msg
	}
	return v.Command(fmt.Sprintf("echon '%s: ' | echohl %s | echon 'SUCCESS' | echohl None | echon '%s'", prefix, success, msg))
}

// ReportError output of the accumulated errors report.
// TODO(zchee): research vim.ReportError behavior
// Why it does not immediately display error?
func ReportError(v *vim.Vim, format string, a ...interface{}) error {
	return v.ReportError(fmt.Sprintf(format, a...))
}

// ClearMsg cleanups the echo area.
func ClearMsg(v *vim.Vim) error {
	return v.Command("echon")
}
<commit_msg>nvim/echo: Add EchoerrWrap and Fix EchohlErr to use Sprintf<commit_after>// Copyright 2016 Koichi Shiraishi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nvim

import (
	"fmt"
	"os"
	"strings"

	"github.com/garyburd/neovim-go/vim"
	"github.com/pkg/errors"
)

var (
	errorColor = "Identifier"
	progress   = "Identifier"
	success    = "Function"
)

// Echo provide the vim 'echo' command.
func Echo(v *vim.Vim, format string, a ...interface{}) error {
	return v.Command("echo '" + fmt.Sprintf(format, a...) + "'")
}

// EchoRaw provide the raw output vim 'echo' command.
func EchoRaw(v *vim.Vim, a string) error {
	return v.Command("echo \"" + a + "\"")
}

// Echomsg provide the vim 'echomsg' command.
func Echomsg(v *vim.Vim, a ...interface{}) error {
	return v.Command("echomsg '" + strings.TrimSpace(fmt.Sprintln(a...)) + "'")
}

// Echoerr provide the vim 'echoerr' command.
func Echoerr(v *vim.Vim, format string, a ...interface{}) error {
	return v.Command("echoerr '" + fmt.Sprintf(format, a...) + "'")
}

// EchoerrWrap splits the errors.Wrap's cause and error messages,
// and provide the vim 'echo' message with 'echohl' highlighting to cause text.
func EchoerrWrap(v *vim.Vim, err error) error {
	v.Command("redraw")
	er := strings.SplitAfterN(fmt.Sprintf("%s", err), ": ", 2)
	if os.Getenv("NVIM_GO_DEBUG") != "" {
		errors.Fprint(os.Stderr, err)
	}
	return v.Command("echo \"" + er[0] + "\" | echohl " + errorColor + " | echon \"" + er[1] + "\" | echohl None")
}

// EchohlErr provide the vim 'echo' command with the 'echohl' highlighting prefix text.
func EchohlErr(v *vim.Vim, prefix string, a ...interface{}) error {
	v.Command("redraw")
	if prefix != "" {
		prefix += ": "
	}
	er := fmt.Sprintf("%s", a...)
	return v.Command("echo '" + prefix + "' | echohl " + errorColor + " | echon \"" + er + "\" | echohl None")
}

// EchohlBefore provide the vim 'echo' command with the 'echohl' highlighting prefix text.
func EchohlBefore(v *vim.Vim, prefix string, highlight string, format string, a ...interface{}) error {
	v.Command("redraw")
	suffix := "' | echohl None | echon '"
	if prefix != "" {
		suffix += ": "
	}
	return v.Command("echohl " + highlight + " | echo '" + prefix + suffix + fmt.Sprintf(format, a...) + "'")
}

// EchohlAfter provide the vim 'echo' command with the 'echohl' highlighting message text.
func EchohlAfter(v *vim.Vim, prefix string, highlight string, format string, a ...interface{}) error {
	v.Command("redraw")
	if prefix != "" {
		prefix += ": "
	}
	return v.Command("echo '" + prefix + "' | echohl " + highlight + " | echon '" + fmt.Sprintf(format, a...) + "' | echohl None")
}

// EchoProgress displays a command progress message to echo area.
func EchoProgress(v *vim.Vim, prefix, before, from, to string) error {
	v.Command("redraw")
	if prefix != "" {
		prefix += ": "
	}
	// TODO(zchee): Refactoring because line too long.
	return v.Command(fmt.Sprintf("echon '%s%s ' | echohl %s | echon '%s' | echohl None | echon ' to ' | echohl %s | echon '%s' | echohl None | echon ' ...'", prefix, before, progress, from, progress, to))
}

// EchoSuccess displays the success of the command to echo area.
func EchoSuccess(v *vim.Vim, prefix string, msg string) error {
	v.Command("redraw")
	if msg != "" {
		msg = " " + msg
	}
	return v.Command(fmt.Sprintf("echon '%s: ' | echohl %s | echon 'SUCCESS' | echohl None | echon '%s'", prefix, success, msg))
}

// ReportError output of the accumulated errors report.
// TODO(zchee): research vim.ReportError behavior
// Why it does not immediately display error?
func ReportError(v *vim.Vim, format string, a ...interface{}) error {
	return v.ReportError(fmt.Sprintf(format, a...))
}

// ClearMsg cleanups the echo area.
func ClearMsg(v *vim.Vim) error {
	return v.Command("echon")
}
<|endoftext|>


<commit_before>// apgar-server
//
// Author: Joe Block <joe.block@daqri.com>
//
// Extremely minimalist http server to serve health check status. We don't
// need or want any fancy url rewriting, cgis, just to serve up a directory
// of text files.
//
// The MIT License (MIT)
//
// Copyright (c) 2016 Daqri, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"github.com/BurntSushi/toml"
)

type Tomlmap struct {
  Webserver webserver
}

type webserver struct {
	Ipaddress  string
	Port  string
}

func main() {
	raw := os.Getpid()
	myPid := []byte(strconv.Itoa(raw))
	var conf Tomlmap
	err := ioutil.WriteFile("/var/run/apgar-server.pid", myPid, 0644)
	if err != nil {
		fmt.Println("Could not write /var/run/apgar-server.pid:", err)
	}
	if _, err := toml.DecodeFile("/etc/apgar/config.toml", &conf); err != nil {
		fmt.Println("Could not open /etc/apgar/config.toml fallback to defaults", err)
	}
	webserverIP := conf.Webserver.Ipaddress
	webserverPort := conf.Webserver.Port
		if len(strings.TrimSpace(webserverIP)) == 0 {
	 webserverIP = ""
	}
	if len(strings.TrimSpace(webserverPort)) == 0 {
	webserverPort = "9000"
	}
	listenAddress := fmt.Sprintf("%s:%s", webserverIP, webserverPort)
	http.HandleFunc("/status", healthCheck)
	http.HandleFunc("/", baseHandler)
	http.ListenAndServe(listenAddress, nil)
}

// Handy to allow our services to display scrapable data by writing to
// /var/lib/apgar/foo
func baseHandler(w http.ResponseWriter, r *http.Request) {
	fileName := fmt.Sprintf("/var/lib/apgar%s", r.URL)
	data, err := ioutil.ReadFile(fileName)

	w.Header().Set("Content-Type", "text/plain")

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(fmt.Sprintf("%s", err)))
	} else {
		w.WriteHeader(http.StatusOK)
		w.Write(data)
	}
}

// Our healthcheck status is stored in /var/lib/apgar/status
// Validate that we are healthy, set proper http response if not
func healthCheck(w http.ResponseWriter, r *http.Request) {
	var healthy bool

	b, err := ioutil.ReadFile("/var/lib/apgar/status")

	// If error (such as file not found), report unhealthy
	if err != nil {
		fmt.Println(err)
		healthy = false
	} else {
		s := string(b)
		healthy = !strings.Contains(s, "UNHEALTHY")
	}

	w.Header().Set("Content-Type", "text/plain")

	if healthy {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("HEALTHY\n"))
	} else {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("UNHEALTHY\n"))
	}
}
<commit_msg>Fixed formatting<commit_after>// apgar-server
//
// Author: Joe Block <joe.block@daqri.com>
//
// Extremely minimalist http server to serve health check status. We don't
// need or want any fancy url rewriting, cgis, just to serve up a directory
// of text files.
//
// The MIT License (MIT)
//
// Copyright (c) 2016 Daqri, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

package main

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
)

type Tomlmap struct {
	Webserver webserver
}

type webserver struct {
	Ipaddress string
	Port      string
}

func main() {
	raw := os.Getpid()
	myPid := []byte(strconv.Itoa(raw))
	var conf Tomlmap
	err := ioutil.WriteFile("/var/run/apgar-server.pid", myPid, 0644)
	if err != nil {
		fmt.Println("Could not write /var/run/apgar-server.pid:", err)
	}
	if _, err := toml.DecodeFile("/etc/apgar/config.toml", &conf); err != nil {
		fmt.Println("Could not open /etc/apgar/config.toml fallback to defaults", err)
	}
	webserverIP := conf.Webserver.Ipaddress
	webserverPort := conf.Webserver.Port
	if len(strings.TrimSpace(webserverIP)) == 0 {
		webserverIP = ""
	}
	if len(strings.TrimSpace(webserverPort)) == 0 {
		webserverPort = "9000"
	}
	listenAddress := fmt.Sprintf("%s:%s", webserverIP, webserverPort)
	http.HandleFunc("/status", healthCheck)
	http.HandleFunc("/", baseHandler)
	http.ListenAndServe(listenAddress, nil)
}

// Handy to allow our services to display scrapable data by writing to
// /var/lib/apgar/foo
func baseHandler(w http.ResponseWriter, r *http.Request) {
	fileName := fmt.Sprintf("/var/lib/apgar%s", r.URL)
	data, err := ioutil.ReadFile(fileName)

	w.Header().Set("Content-Type", "text/plain")

	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(fmt.Sprintf("%s", err)))
	} else {
		w.WriteHeader(http.StatusOK)
		w.Write(data)
	}
}

// Our healthcheck status is stored in /var/lib/apgar/status
// Validate that we are healthy, set proper http response if not
func healthCheck(w http.ResponseWriter, r *http.Request) {
	var healthy bool

	b, err := ioutil.ReadFile("/var/lib/apgar/status")

	// If error (such as file not found), report unhealthy
	if err != nil {
		fmt.Println(err)
		healthy = false
	} else {
		s := string(b)
		healthy = !strings.Contains(s, "UNHEALTHY")
	}

	w.Header().Set("Content-Type", "text/plain")

	if healthy {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("HEALTHY\n"))
	} else {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("UNHEALTHY\n"))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"
)

const (
	OpGet         = "/get"
	OpSet         = "/post"
	OpInfo        = "._info"
	OpDelete      = "/delete"
	OpCursorFirst = "_first"
	OpCursorLast  = "_last"
	OpCursorNext  = "._next"
	OpCursorPrev  = "._prev"
	OpPrefixMatch = "._match"
)

func httpGet(w http.ResponseWriter, op Operation) *HTTPError {
	value := op.Database.Unjar(op.Key)
	// Check if the item existed
	if value == nil {
		return &HTTPError{Code: 404, Message: "These aren't your ghosts."}
	}

	// Send value
	w.Write(value)
	return nil
}

func httpSet(w http.ResponseWriter, op Operation, r *http.Request) *HTTPError {
	value, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return &HTTPError{Code: 500, Message: "Your post body is messed up!"}
	}

	// Check if value already existed
	exists := op.Database.Exists(op.Key)

	res := op.Database.Jar(op.Key, value)
	if res == 0 {
		// Status 201 if created, 200 if updated
		if exists {
			w.WriteHeader(200)
		} else {
			w.WriteHeader(201)
		}
		fmt.Fprintf(w, "無駄")
	} else {
		return &HTTPError{Code: 500, Message: "Something went horribly wrong..."}
	}

	// Try to set expiration, if provided
	if eep, ok := r.Header["X-Olegdb-Use-By"]; ok {
		ep, err := strconv.Atoi(eep[0])
		if err != nil {
			return &HTTPError{Code: 500, Message: "The expiration format is wrong!"}
		}
		date := time.Unix(int64(ep), 0)
		op.Database.Spoil(op.Key, date)
		// fmt.Fprintf(w, "\r\nThe jar is spoiling!")
	}

	return nil
}

func httpInfo(w http.ResponseWriter, op Operation) *HTTPError {
	// Does it even exists?
	if !op.Database.Exists(op.Key) {
		return &HTTPError{Code: 404, Message: "Key not found in database"}
	}

	// Get and set Expiration
	res, doesExpire := op.Database.Expiration(op.Key)
	if doesExpire {
		w.Header().Add("Expires", strconv.Itoa(int(res.UTC().Unix())))
	}

	// Add Record count
	w.Header().Add("X-Olegdb-Rcrd-Cnt", strconv.Itoa(int(*op.Database.RecordCount)))

	// Send empty body
	fmt.Fprintf(w, "\r\n")
	return nil
}

func httpDelete(w http.ResponseWriter, op Operation) *HTTPError {
	res := op.Database.Scoop(op.Key)
	if res != 0 {
		return &HTTPError{Code: 500, Message: "Something went horribly wrong..."}
	}

	fmt.Fprintf(w, "Key deleted successfully!")
	return nil
}

func httpMatch(w http.ResponseWriter, op Operation) *HTTPError {
	has, res := op.Database.PrefixMatch(op.Key)
	if !has {
		return &HTTPError{Code: 404, Message: "No matches found"}
	}
	w.Header().Add("X-Olegdb-Num-Matches", strconv.Itoa(len(res)))
	fmt.Fprintf(w, strings.Join(res, "\n"))
	return nil
}

func httpCurFirst(w http.ResponseWriter, op Operation) *HTTPError {
	has, key, data := op.Database.First()
	if !has {
		return &HTTPError{Code: 404, Message: "No records found"}
	}
	w.Header().Add("X-Olegdb-Key", key)
	w.Write(data)
	return nil
}

func httpCurLast(w http.ResponseWriter, op Operation) *HTTPError {
	has, key, data := op.Database.Last()
	if !has {
		return &HTTPError{Code: 404, Message: "No records found"}
	}
	w.Header().Add("X-Olegdb-Key", key)
	w.Write(data)
	return nil
}

func httpCurNext(w http.ResponseWriter, op Operation) *HTTPError {
	has, key, data := op.Database.Next(op.Key)
	if !has {
		return &HTTPError{Code: 404, Message: "No records found"}
	}
	w.Header().Add("X-Olegdb-Key", key)
	w.Write(data)
	return nil
}

func httpCurPrev(w http.ResponseWriter, op Operation) *HTTPError {
	has, key, data := op.Database.Prev(op.Key)
	if !has {
		return &HTTPError{Code: 404, Message: "No records found"}
	}
	w.Header().Add("X-Olegdb-Key", key)
	w.Write(data)
	return nil
}
<commit_msg>Set content-length header on prefix matching.<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"
)

const (
	OpGet         = "/get"
	OpSet         = "/post"
	OpInfo        = "._info"
	OpDelete      = "/delete"
	OpCursorFirst = "_first"
	OpCursorLast  = "_last"
	OpCursorNext  = "._next"
	OpCursorPrev  = "._prev"
	OpPrefixMatch = "._match"
)

func httpGet(w http.ResponseWriter, op Operation) *HTTPError {
	value := op.Database.Unjar(op.Key)
	// Check if the item existed
	if value == nil {
		return &HTTPError{Code: 404, Message: "These aren't your ghosts."}
	}

	// Send value
	w.Write(value)
	return nil
}

func httpSet(w http.ResponseWriter, op Operation, r *http.Request) *HTTPError {
	value, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return &HTTPError{Code: 500, Message: "Your post body is messed up!"}
	}

	// Check if value already existed
	exists := op.Database.Exists(op.Key)

	res := op.Database.Jar(op.Key, value)
	if res == 0 {
		// Status 201 if created, 200 if updated
		if exists {
			w.WriteHeader(200)
		} else {
			w.WriteHeader(201)
		}
		fmt.Fprintf(w, "無駄")
	} else {
		return &HTTPError{Code: 500, Message: "Something went horribly wrong..."}
	}

	// Try to set expiration, if provided
	if eep, ok := r.Header["X-Olegdb-Use-By"]; ok {
		ep, err := strconv.Atoi(eep[0])
		if err != nil {
			return &HTTPError{Code: 500, Message: "The expiration format is wrong!"}
		}
		date := time.Unix(int64(ep), 0)
		op.Database.Spoil(op.Key, date)
		// fmt.Fprintf(w, "\r\nThe jar is spoiling!")
	}

	return nil
}

func httpInfo(w http.ResponseWriter, op Operation) *HTTPError {
	// Does it even exists?
	if !op.Database.Exists(op.Key) {
		return &HTTPError{Code: 404, Message: "Key not found in database"}
	}

	// Get and set Expiration
	res, doesExpire := op.Database.Expiration(op.Key)
	if doesExpire {
		w.Header().Add("Expires", strconv.Itoa(int(res.UTC().Unix())))
	}

	// Add Record count
	w.Header().Add("X-Olegdb-Rcrd-Cnt", strconv.Itoa(int(*op.Database.RecordCount)))

	// Send empty body
	fmt.Fprintf(w, "\r\n")
	return nil
}

func httpDelete(w http.ResponseWriter, op Operation) *HTTPError {
	res := op.Database.Scoop(op.Key)
	if res != 0 {
		return &HTTPError{Code: 500, Message: "Something went horribly wrong..."}
	}

	fmt.Fprintf(w, "Key deleted successfully!")
	return nil
}

func httpMatch(w http.ResponseWriter, op Operation) *HTTPError {
	has, res := op.Database.PrefixMatch(op.Key)
	if !has {
		return &HTTPError{Code: 404, Message: "No matches found"}
	}
	w.Header().Add("X-Olegdb-Num-Matches", strconv.Itoa(len(res)))
	content := strings.Join(res, "\n")
	w.Header().Add("Content-Length", strconv.Itoa(len(content)))
	fmt.Fprintf(w, content)
	return nil
}

func httpCurFirst(w http.ResponseWriter, op Operation) *HTTPError {
	has, key, data := op.Database.First()
	if !has {
		return &HTTPError{Code: 404, Message: "No records found"}
	}
	w.Header().Add("X-Olegdb-Key", key)
	w.Write(data)
	return nil
}

func httpCurLast(w http.ResponseWriter, op Operation) *HTTPError {
	has, key, data := op.Database.Last()
	if !has {
		return &HTTPError{Code: 404, Message: "No records found"}
	}
	w.Header().Add("X-Olegdb-Key", key)
	w.Write(data)
	return nil
}

func httpCurNext(w http.ResponseWriter, op Operation) *HTTPError {
	has, key, data := op.Database.Next(op.Key)
	if !has {
		return &HTTPError{Code: 404, Message: "No records found"}
	}
	w.Header().Add("X-Olegdb-Key", key)
	w.Write(data)
	return nil
}

func httpCurPrev(w http.ResponseWriter, op Operation) *HTTPError {
	has, key, data := op.Database.Prev(op.Key)
	if !has {
		return &HTTPError{Code: 404, Message: "No records found"}
	}
	w.Header().Add("X-Olegdb-Key", key)
	w.Write(data)
	return nil
}
<|endoftext|>