<commit_before>package z

import (
	"fmt"
	"log"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
)

// SuperFlagHelp makes it really easy to generate command line `--help` output for a SuperFlag. For
// example:
//
//	const flagDefaults = `enabled=true; path=some/path;`
//
//	var help string = z.NewSuperFlagHelp(flagDefaults).
//		Flag("enabled", "Turns on <something>.").
//		Flag("path", "The path to <something>.").
//		Flag("another", "Not present in defaults, but still included.").
//		String()
//
// The `help` string would then contain:
//
//	enabled=true; Turns on <something>.
//	path=some/path; The path to <something>.
//	another=; Not present in defaults, but still included.
//
// All flags are sorted alphabetically for consistent `--help` output. Flags with default values are
// placed at the top, and everything else goes under.
type SuperFlagHelp struct {
	head     string
	defaults *SuperFlag
	flags    map[string]string
}

func NewSuperFlagHelp(defaults string) *SuperFlagHelp {
	return &SuperFlagHelp{
		defaults: NewSuperFlag(defaults),
		flags:    make(map[string]string, 0),
	}
}

func (h *SuperFlagHelp) Head(head string) *SuperFlagHelp {
	h.head = head
	return h
}

func (h *SuperFlagHelp) Flag(name, description string) *SuperFlagHelp {
	h.flags[name] = description
	return h
}

func (h *SuperFlagHelp) String() string {
	defaultLines := make([]string, 0)
	otherLines := make([]string, 0)
	for name, help := range h.flags {
		val, found := h.defaults.m[name]
		line := fmt.Sprintf("    %s=%s; %s\n", name, val, help)
		if found {
			defaultLines = append(defaultLines, line)
		} else {
			otherLines = append(otherLines, line)
		}
	}
	sort.Strings(defaultLines)
	sort.Strings(otherLines)
	dls := strings.Join(defaultLines, "")
	ols := strings.Join(otherLines, "")
	if len(ols) == 0 {
		// remove last newline
		dls = dls[:len(dls)-1]
	}
	// remove last newline
	if len(ols) > 1 {
		ols = ols[:len(ols)-1]
	}
	return h.head + "\n" + dls + ols
}

func parseFlag(flag string) map[string]string {
	kvm := make(map[string]string)
	for _, kv := range strings.Split(flag, ";") {
		if strings.TrimSpace(kv) == "" {
			continue
		}
		splits := strings.SplitN(kv, "=", 2)
		k := strings.TrimSpace(splits[0])
		k = strings.ToLower(k)
		k = strings.ReplaceAll(k, "_", "-")
		kvm[k] = strings.TrimSpace(splits[1])
	}
	return kvm
}

type SuperFlag struct {
	m map[string]string
}

func NewSuperFlag(flag string) *SuperFlag {
	return &SuperFlag{
		m: parseFlag(flag),
	}
}

func (sf *SuperFlag) String() string {
	if sf == nil {
		return ""
	}
	var kvs []string
	for k, v := range sf.m {
		kvs = append(kvs, fmt.Sprintf("%s=%s", k, v))
	}
	return strings.Join(kvs, "; ")
}

func (sf *SuperFlag) MergeAndCheckDefault(flag string) *SuperFlag {
	if sf == nil {
		sf = &SuperFlag{
			m: parseFlag(flag),
		}
		return sf
	}
	numKeys := len(sf.m)
	src := parseFlag(flag)
	for k := range src {
		if _, ok := sf.m[k]; ok {
			numKeys--
		}
	}
	if numKeys != 0 {
		msg := fmt.Sprintf("Found invalid options in %s. Valid options: %v", sf, flag)
		panic(msg)
	}
	for k, v := range src {
		if _, ok := sf.m[k]; !ok {
			sf.m[k] = v
		}
	}
	return sf
}

func (sf *SuperFlag) Has(opt string) bool {
	val := sf.GetString(opt)
	return val != ""
}

func (sf *SuperFlag) GetDuration(opt string) time.Duration {
	val := sf.GetString(opt)
	if val == "" {
		return time.Duration(0)
	}
	if strings.Contains(val, "d") {
		val = strings.Replace(val, "d", "", 1)
		days, err := strconv.ParseUint(val, 0, 64)
		if err != nil {
			return time.Duration(0)
		}
		return time.Hour * 24 * time.Duration(days)
	}
	d, err := time.ParseDuration(val)
	if err != nil {
		return time.Duration(0)
	}
	return d
}

func (sf *SuperFlag) GetBool(opt string) bool {
	val := sf.GetString(opt)
	if val == "" {
		return false
	}
	b, err := strconv.ParseBool(val)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as bool for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return b
}

func (sf *SuperFlag) GetFloat64(opt string) float64 {
	val := sf.GetString(opt)
	if val == "" {
		return 0
	}
	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as float64 for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return f
}

func (sf *SuperFlag) GetInt64(opt string) int64 {
	val := sf.GetString(opt)
	if val == "" {
		return 0
	}
	i, err := strconv.ParseInt(val, 0, 64)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as int64 for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return i
}

func (sf *SuperFlag) GetUint64(opt string) uint64 {
	val := sf.GetString(opt)
	if val == "" {
		return 0
	}
	u, err := strconv.ParseUint(val, 0, 64)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as uint64 for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return u
}

func (sf *SuperFlag) GetUint32(opt string) uint32 {
	val := sf.GetString(opt)
	if val == "" {
		return 0
	}
	u, err := strconv.ParseUint(val, 0, 32)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as uint32 for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return uint32(u)
}

func (sf *SuperFlag) GetString(opt string) string {
	if sf == nil {
		return ""
	}
	return sf.m[opt]
}
<commit_msg>SuperFlagHelp keep newline if defaults non-empty (#253)<commit_after>package z

import (
	"fmt"
	"log"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"
)

// SuperFlagHelp makes it really easy to generate command line `--help` output for a SuperFlag. For
// example:
//
//	const flagDefaults = `enabled=true; path=some/path;`
//
//	var help string = z.NewSuperFlagHelp(flagDefaults).
//		Flag("enabled", "Turns on <something>.").
//		Flag("path", "The path to <something>.").
//		Flag("another", "Not present in defaults, but still included.").
//		String()
//
// The `help` string would then contain:
//
//	enabled=true; Turns on <something>.
//	path=some/path; The path to <something>.
//	another=; Not present in defaults, but still included.
//
// All flags are sorted alphabetically for consistent `--help` output. Flags with default values are
// placed at the top, and everything else goes under.
type SuperFlagHelp struct {
	head     string
	defaults *SuperFlag
	flags    map[string]string
}

func NewSuperFlagHelp(defaults string) *SuperFlagHelp {
	return &SuperFlagHelp{
		defaults: NewSuperFlag(defaults),
		flags:    make(map[string]string, 0),
	}
}

func (h *SuperFlagHelp) Head(head string) *SuperFlagHelp {
	h.head = head
	return h
}

func (h *SuperFlagHelp) Flag(name, description string) *SuperFlagHelp {
	h.flags[name] = description
	return h
}

func (h *SuperFlagHelp) String() string {
	defaultLines := make([]string, 0)
	otherLines := make([]string, 0)
	for name, help := range h.flags {
		val, found := h.defaults.m[name]
		line := fmt.Sprintf("    %s=%s; %s\n", name, val, help)
		if found {
			defaultLines = append(defaultLines, line)
		} else {
			otherLines = append(otherLines, line)
		}
	}
	sort.Strings(defaultLines)
	sort.Strings(otherLines)
	dls := strings.Join(defaultLines, "")
	ols := strings.Join(otherLines, "")
	if len(h.defaults.m) == 0 && len(ols) == 0 {
		// remove last newline
		dls = dls[:len(dls)-1]
	}
	// remove last newline
	if len(h.defaults.m) == 0 && len(ols) > 1 {
		ols = ols[:len(ols)-1]
	}
	return h.head + "\n" + dls + ols
}

func parseFlag(flag string) map[string]string {
	kvm := make(map[string]string)
	for _, kv := range strings.Split(flag, ";") {
		if strings.TrimSpace(kv) == "" {
			continue
		}
		splits := strings.SplitN(kv, "=", 2)
		k := strings.TrimSpace(splits[0])
		k = strings.ToLower(k)
		k = strings.ReplaceAll(k, "_", "-")
		kvm[k] = strings.TrimSpace(splits[1])
	}
	return kvm
}

type SuperFlag struct {
	m map[string]string
}

func NewSuperFlag(flag string) *SuperFlag {
	return &SuperFlag{
		m: parseFlag(flag),
	}
}

func (sf *SuperFlag) String() string {
	if sf == nil {
		return ""
	}
	var kvs []string
	for k, v := range sf.m {
		kvs = append(kvs, fmt.Sprintf("%s=%s", k, v))
	}
	return strings.Join(kvs, "; ")
}

func (sf *SuperFlag) MergeAndCheckDefault(flag string) *SuperFlag {
	if sf == nil {
		sf = &SuperFlag{
			m: parseFlag(flag),
		}
		return sf
	}
	numKeys := len(sf.m)
	src := parseFlag(flag)
	for k := range src {
		if _, ok := sf.m[k]; ok {
			numKeys--
		}
	}
	if numKeys != 0 {
		msg := fmt.Sprintf("Found invalid options in %s. Valid options: %v", sf, flag)
		panic(msg)
	}
	for k, v := range src {
		if _, ok := sf.m[k]; !ok {
			sf.m[k] = v
		}
	}
	return sf
}

func (sf *SuperFlag) Has(opt string) bool {
	val := sf.GetString(opt)
	return val != ""
}

func (sf *SuperFlag) GetDuration(opt string) time.Duration {
	val := sf.GetString(opt)
	if val == "" {
		return time.Duration(0)
	}
	if strings.Contains(val, "d") {
		val = strings.Replace(val, "d", "", 1)
		days, err := strconv.ParseUint(val, 0, 64)
		if err != nil {
			return time.Duration(0)
		}
		return time.Hour * 24 * time.Duration(days)
	}
	d, err := time.ParseDuration(val)
	if err != nil {
		return time.Duration(0)
	}
	return d
}

func (sf *SuperFlag) GetBool(opt string) bool {
	val := sf.GetString(opt)
	if val == "" {
		return false
	}
	b, err := strconv.ParseBool(val)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as bool for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return b
}

func (sf *SuperFlag) GetFloat64(opt string) float64 {
	val := sf.GetString(opt)
	if val == "" {
		return 0
	}
	f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as float64 for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return f
}

func (sf *SuperFlag) GetInt64(opt string) int64 {
	val := sf.GetString(opt)
	if val == "" {
		return 0
	}
	i, err := strconv.ParseInt(val, 0, 64)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as int64 for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return i
}

func (sf *SuperFlag) GetUint64(opt string) uint64 {
	val := sf.GetString(opt)
	if val == "" {
		return 0
	}
	u, err := strconv.ParseUint(val, 0, 64)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as uint64 for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return u
}

func (sf *SuperFlag) GetUint32(opt string) uint32 {
	val := sf.GetString(opt)
	if val == "" {
		return 0
	}
	u, err := strconv.ParseUint(val, 0, 32)
	if err != nil {
		err = errors.Wrapf(err,
			"Unable to parse %s as uint32 for key: %s. Options: %s\n",
			val, opt, sf)
		log.Fatalf("%+v", err)
	}
	return uint32(u)
}

func (sf *SuperFlag) GetString(opt string) string {
	if sf == nil {
		return ""
	}
	return sf.m[opt]
}
<|endoftext|>


<commit_before>package ingestor

import (
	"cred-alert/metrics"
	"cred-alert/queue"

	"github.com/pivotal-golang/lager"
)

//go:generate counterfeiter . Ingestor

type Ingestor interface {
	IngestPushScan(lager.Logger, PushScan, string) error
}

type ingestor struct {
	whitelist *Whitelist
	taskQueue queue.Queue
	generator queue.UUIDGenerator

	requestCounter      metrics.Counter
	ignoredEventCounter metrics.Counter
}

func NewIngestor(
	taskQueue queue.Queue,
	emitter metrics.Emitter,
	whitelist *Whitelist,
	generator queue.UUIDGenerator,
) *ingestor {
	requestCounter := emitter.Counter("cred_alert.ingestor_requests")
	ignoredEventCounter := emitter.Counter("cred_alert.ignored_events")

	handler := &ingestor{
		taskQueue:           taskQueue,
		whitelist:           whitelist,
		generator:           generator,
		requestCounter:      requestCounter,
		ignoredEventCounter: ignoredEventCounter,
	}

	return handler
}

func (s *ingestor) IngestPushScan(logger lager.Logger, scan PushScan, githubID string) error {
	logger = logger.Session("ingest")

	if scan.Private && s.whitelist.IsIgnored(scan.Repository) {
		logger.Info("ignoring-repo", lager.Data{
			"repo": scan.Repository,
		})

		s.ignoredEventCounter.Inc(logger)

		return nil
	}

	s.requestCounter.Inc(logger)

	id := s.generator.Generate()

	task := queue.PushEventPlan{
		Owner:      scan.Owner,
		Repository: scan.Repository,
		From:       scan.From,
		To:         scan.To,
	}.Task(id)

	logger = logger.Session("enqueuing-task", lager.Data{
		"task-id":   id,
		"github-id": githubID,
	})

	err := s.taskQueue.Enqueue(task)
	if err != nil {
		logger.Error("failed", err)
		return err
	}

	logger.Info("done")

	return nil
}
<commit_msg>update logging in ingestor<commit_after>package ingestor

import (
	"cred-alert/metrics"
	"cred-alert/queue"

	"github.com/pivotal-golang/lager"
)

//go:generate counterfeiter . Ingestor

type Ingestor interface {
	IngestPushScan(lager.Logger, PushScan, string) error
}

type ingestor struct {
	whitelist *Whitelist
	taskQueue queue.Queue
	generator queue.UUIDGenerator

	requestCounter      metrics.Counter
	ignoredEventCounter metrics.Counter
}

func NewIngestor(
	taskQueue queue.Queue,
	emitter metrics.Emitter,
	whitelist *Whitelist,
	generator queue.UUIDGenerator,
) *ingestor {
	requestCounter := emitter.Counter("cred_alert.ingestor_requests")
	ignoredEventCounter := emitter.Counter("cred_alert.ignored_events")

	handler := &ingestor{
		taskQueue:           taskQueue,
		whitelist:           whitelist,
		generator:           generator,
		requestCounter:      requestCounter,
		ignoredEventCounter: ignoredEventCounter,
	}

	return handler
}

func (s *ingestor) IngestPushScan(logger lager.Logger, scan PushScan, githubID string) error {
	logger = logger.Session("ingest-push-scan")
	logger.Info("starting")

	if scan.Private && s.whitelist.IsIgnored(scan.Repository) {
		logger.Info("ignoring-repo", lager.Data{
			"repo": scan.Repository,
		})

		s.ignoredEventCounter.Inc(logger)

		logger.Info("done")
		return nil
	}

	s.requestCounter.Inc(logger)

	id := s.generator.Generate()

	task := queue.PushEventPlan{
		Owner:      scan.Owner,
		Repository: scan.Repository,
		From:       scan.From,
		To:         scan.To,
	}.Task(id)

	logger = logger.Session("enqueuing-task", lager.Data{
		"task-id":   id,
		"github-id": githubID,
	})

	err := s.taskQueue.Enqueue(task)
	if err != nil {
		logger.Error("failed", err)
		return err
	}

	logger.Info("done")
	return nil
}
<|endoftext|>


<commit_before>package rainforest

import (
	"crypto/md5"
	"io/ioutil"
	"mime"
	"os"
	"strconv"
)

// UploadedFile represents a file that has been uploaded to Rainforest
type UploadedFile struct {
	ID        int    `json:"id"`
	Signature string `json:"signature"`
	Digest    string `json:"digest"`
	MimeType  string `json:"mime_type"`
	Size      int64  `json:"size"`
	Name      string `json:"name"`
}

// GetUploadedFiles returns information for all all files uploaded to the
// given test before.
func (c *Client) GetUploadedFiles(fileID int) ([]UploadedFile, error) {
	req, err := c.NewRequest("GET", "tests/"+strconv.Itoa(fileID)+"/files", nil)
	if err != nil {
		return nil, err
	}

	var fileResp []UploadedFile
	_, err = c.Do(req, &fileResp)
	return fileResp, err
}

// AWSFileInfo represents the response when uploading new file data to Rainforest.
// It contains information used to upload data the file to AWS.
type AWSFileInfo struct {
	FileID        int    `json:"file_id"`
	FileSignature string `json:"file_signature"`
	AWSURL        string `json:"aws_url"`
	AWSKey        string `json:"aws_key"`
	AWSAccessID   string `json:"aws_access_id"`
	AWSPolicy     string `json:"aws_policy"`
	AWSACL        string `json:"aws_acl"`
	AWSSignature  string `json:"aws_signature"`
}

// CreateFile creates a UploadedFile resource by sending file information to
// Rainforest. This information is used for uploading the actual file to AWS.
func (c *Client) CreateFile(testID int, file os.File) (*AWSFileInfo, error) {
	var awsFileInfo *AWSFileInfo
	fileInfo, err := os.Stat(file.Name())

	if err != nil {
		return awsFileInfo, err
	}

	data, err := ioutil.ReadAll(&file)
	if err != nil {
		return awsFileInfo, err
	}

	md5CheckSum := md5.Sum(data)

	body := UploadedFile{
		MimeType: mime.TypeByExtension(fileInfo.Name()),
		Size:     fileInfo.Size(),
		Name:     fileInfo.Name(),
		Digest:   string(md5CheckSum[:16]),
	}

	url := "tests/" + strconv.Itoa(testID) + "/files"
	req, err := c.NewRequest("POST", url, body)
	if err != nil {
		return awsFileInfo, err
	}

	_, err = c.Do(req, awsFileInfo)
	return awsFileInfo, err
}
<commit_msg>fix upload payload and marshalling json response<commit_after>package rainforest

import (
	"crypto/md5"
	"encoding/hex"
	"io/ioutil"
	"mime"
	"os"
	"path/filepath"
	"strconv"
)

// UploadedFile represents a file that has been uploaded to Rainforest
type UploadedFile struct {
	ID        int    `json:"id"`
	Signature string `json:"signature"`
	Digest    string `json:"digest"`
	MimeType  string `json:"mime_type"`
	Size      int64  `json:"size"`
	Name      string `json:"name"`
}

// GetUploadedFiles returns information for all all files uploaded to the
// given test before.
func (c *Client) GetUploadedFiles(fileID int) ([]UploadedFile, error) {
	req, err := c.NewRequest("GET", "tests/"+strconv.Itoa(fileID)+"/files", nil)
	if err != nil {
		return nil, err
	}

	var fileResp []UploadedFile
	_, err = c.Do(req, &fileResp)
	return fileResp, err
}

// AWSFileInfo represents the response when uploading new file data to Rainforest.
// It contains information used to upload data the file to AWS.
type AWSFileInfo struct {
	FileID        int    `json:"file_id"`
	FileSignature string `json:"file_signature"`
	AWSURL        string `json:"aws_url"`
	AWSKey        string `json:"aws_key"`
	AWSAccessID   string `json:"aws_access_id"`
	AWSPolicy     string `json:"aws_policy"`
	AWSACL        string `json:"aws_acl"`
	AWSSignature  string `json:"aws_signature"`
}

// CreateFile creates a UploadedFile resource by sending file information to
// Rainforest. This information is used for uploading the actual file to AWS.
func (c *Client) CreateFile(testID int, file *os.File) (*AWSFileInfo, error) {
	awsFileInfo := &AWSFileInfo{}
	fileName := file.Name()
	fileInfo, err := os.Stat(fileName)

	if err != nil {
		return awsFileInfo, err
	}

	data, err := ioutil.ReadAll(file)
	if err != nil {
		return awsFileInfo, err
	}

	md5CheckSum := md5.Sum(data)
	hexDigest := hex.EncodeToString(md5CheckSum[:16])

	body := UploadedFile{
		MimeType: mime.TypeByExtension(filepath.Ext(fileName)),
		Size:     fileInfo.Size(),
		Name:     fileName,
		Digest:   hexDigest,
	}

	url := "tests/" + strconv.Itoa(testID) + "/files"
	req, err := c.NewRequest("POST", url, body)
	if err != nil {
		return awsFileInfo, err
	}

	_, err = c.Do(req, awsFileInfo)
	return awsFileInfo, err
}
<|endoftext|>


<commit_before>package tme

import (
	"sync"
	"testing"
	"time"
)

func TestRealTickerSynchronousTicks(t *testing.T) {
	const dur = 1 * time.Millisecond
	const numChecks = 5
	ticker := NewRealTicker(dur, func() {})
	defer ticker.Stop()
	ch := ticker.Chan()
	for i := 0; i < numChecks; i++ {
		select {
		case <-ch:
		case <-time.After(dur * 2):
			t.Fatalf("ticker didn't tick within %s (thread %d)", dur*2, i)
		}
	}
}

func TestRealTickerBroadcastTicker(t *testing.T) {
	const dur = 10 * time.Millisecond
	const numChecks = 5
	const numThreads = 10
	ticker := NewRealTicker(dur, func() {})
	defer ticker.Stop()
	ch := ticker.Chan()
	var wg sync.WaitGroup
	for i := 0; i < numThreads; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			select {
			case <-ch:
			case <-time.After(dur * 10):
				t.Errorf("ticker didn't tick within %s (thread %d)", dur*10, i)
			}
		}(i)
	}
	wg.Wait()
}

func TestRealTickerStop(t *testing.T) {
	const dur = 10 * time.Millisecond
	ticker := NewRealTicker(dur, func() {})
	bch := ticker.Chan()
	select {
	case <-bch:
	case <-time.After(dur * 2):
		t.Errorf("ticker didn't tick within %s", dur*2)
	}
	if ticker.Stop() != true {
		t.Errorf("ticker was already marked stopped")
	}
	select {
	case <-bch:
		t.Errorf("ticker ticked after stop called")
	case <-time.After(dur * 2):
	}
	select {
	case <-ticker.Chan():
		t.Errorf("new channel from ticker ticked after stop called")
	case <-time.After(dur * 2):
	}
}
<commit_msg>rm useless test<commit_after>package tme

import (
	"testing"
	"time"
)

func TestRealTickerSynchronousTicks(t *testing.T) {
	const dur = 1 * time.Millisecond
	const numChecks = 5
	ticker := NewRealTicker(dur, func() {})
	defer ticker.Stop()
	ch := ticker.Chan()
	for i := 0; i < numChecks; i++ {
		select {
		case <-ch:
		case <-time.After(dur * 2):
			t.Fatalf("ticker didn't tick within %s (thread %d)", dur*2, i)
		}
	}
}

func TestRealTickerStop(t *testing.T) {
	const dur = 10 * time.Millisecond
	ticker := NewRealTicker(dur, func() {})
	bch := ticker.Chan()
	select {
	case <-bch:
	case <-time.After(dur * 2):
		t.Errorf("ticker didn't tick within %s", dur*2)
	}
	if ticker.Stop() != true {
		t.Errorf("ticker was already marked stopped")
	}
	select {
	case <-bch:
		t.Errorf("ticker ticked after stop called")
	case <-time.After(dur * 2):
	}
	select {
	case <-ticker.Chan():
		t.Errorf("new channel from ticker ticked after stop called")
	case <-time.After(dur * 2):
	}
}
<|endoftext|>


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

package gcp

import (
	"strings"
	"testing"

	"reflect"

	"gopkg.in/square/go-jose.v1/json"
	"upspin.io/cloud/storage/storagetest"
	"upspin.io/context"
	"upspin.io/upspin"
)

const isAdmin = true

func TestInvalidUser(t *testing.T) {
	u := newDummyKeyServer()
	_, err := u.Lookup("a")
	if err == nil {
		t.Fatal("Expected an error")
	}
	expected := "invalid user"
	if !strings.Contains(err.Error(), expected) {
		t.Errorf("Expected %q, got %q", expected, err)
	}
}

func TestPutOtherUserNotAdmin(t *testing.T) {
	const (
		myName    = "cool@dude.com"
		otherUser = "uncool@buddy.com"
	)

	// Pre-existing user: myName, who *is not* an admin.
	user := &upspin.User{
		Name: myName,
	}
	buf := marshalUser(t, user, !isAdmin)

	// Create a server authenticated with myName and with a pre-existing User entry for myName.
	u, mockGCP := newKeyServerWithMocking(myName, myName, buf)

	// myName now attempts to write somebody else's information.
	otherU := &upspin.User{
		Name:      otherUser,
		PublicKey: upspin.PublicKey("going to change your key, haha"),
	}
	err := u.Put(otherU)
	if err == nil {
		t.Fatal("Expected error, got none")
	}
	if !strings.Contains(err.Error(), "permission denied") {
		t.Errorf("Expected permission denied, got %q", err)
	}
	// Check that indeed we did not write to GCP.
	if len(mockGCP.PutRef) != 0 {
		t.Errorf("Expected no writes, got %d", len(mockGCP.PutRef))
	}
}

func TestPutOtherUserIsAdmin(t *testing.T) {
	const (
		myName    = "cool@dude.com"
		otherUser = "uncool@buddy.com"
	)

	// Pre-existing user: myName, who *is* an admin.
	user := &upspin.User{
		Name: myName,
	}
	buf := marshalUser(t, user, isAdmin)

	// Create a server authenticated with myName and with a pre-existing User entry for myName.
	u, mockGCP := newKeyServerWithMocking(myName, myName, buf)

	// myName now attempts to write somebody else's information.
	otherU := &upspin.User{
		Name:      otherUser,
		PublicKey: upspin.PublicKey("going to change your key, because I can"),
	}
	err := u.Put(otherU)
	if err != nil {
		t.Fatal("Expected no error")
	}
	// Check new user was written to GCP
	if len(mockGCP.PutRef) != 1 {
		t.Fatalf("Expected one write, got %d", len(mockGCP.PutRef))
	}
	if mockGCP.PutRef[0] != otherUser {
		t.Errorf("Expected write to user %q, wrote to %q", otherUser, mockGCP.PutRef[0])
	}
	savedUser, isAdmin := unmarshalUser(t, mockGCP.PutContents[0])
	if !reflect.DeepEqual(*savedUser, *otherU) {
		t.Errorf("Expected Put to store User %v, but it stored %v", otherU, savedUser)
	}
	if isAdmin {
		t.Error("Expected user not to be an admin")
	}
}

func TestPutNewUserSelf(t *testing.T) {
	const myName = "cool@dude.com"

	// New server for myName.
	u, mockGCP := newKeyServerWithMocking(myName, "", nil)

	user := &upspin.User{
		Name: myName,
		Dirs: []upspin.Endpoint{
			upspin.Endpoint{
				Transport: upspin.Remote,
				NetAddr:   upspin.NetAddr("there.co.uk"),
			},
		},
		Stores: []upspin.Endpoint{
			upspin.Endpoint{
				Transport: upspin.Remote,
				NetAddr:   upspin.NetAddr("down-under.au"),
			},
		},
		PublicKey: upspin.PublicKey("my key"),
	}
	err := u.Put(user)
	if err != nil {
		t.Fatal(err)
	}

	// Verify that GCP received the Put.
	if len(mockGCP.PutRef) != 1 || len(mockGCP.PutContents) != 1 {
		t.Fatalf("Expected 1 call to GCP.Put, got %d", len(mockGCP.PutRef))
	}
	if mockGCP.PutRef[0] != myName {
		t.Errorf("Expected write to %q, wrote to %q", myName, mockGCP.PutRef)
	}
	savedUser, isAdmin := unmarshalUser(t, mockGCP.PutContents[0])
	if !reflect.DeepEqual(*savedUser, *user) {
		t.Errorf("Expected Put to store User %v, but it stored %v", user, savedUser)
	}
	if isAdmin {
		t.Error("Expected user not to be an admin")
	}
}

func TestPutNewUserSelfIsAdmin(t *testing.T) {
	const myName = "cool@dude.com"

	user := &upspin.User{
		Name: myName,
		Stores: []upspin.Endpoint{
			upspin.Endpoint{
				Transport: upspin.Remote,
				NetAddr:   upspin.NetAddr("some.place:443"),
			},
		},
		PublicKey: upspin.PublicKey("super secure"),
	}
	buf := marshalUser(t, user, isAdmin)

	// New server for myName.
	u, mockGCP := newKeyServerWithMocking(myName, myName, buf)

	// Changing my user info to include a root dir.
	user.Dirs = append(user.Dirs, upspin.Endpoint{
		Transport: upspin.Remote,
		NetAddr:   upspin.NetAddr("my-root-dir:443"),
	})
	// Change my information.
	err := u.Put(user)
	if err != nil {
		t.Fatal(err)
	}

	// Verify that GCP received the Put.
	if len(mockGCP.PutRef) != 1 || len(mockGCP.PutContents) != 1 {
		t.Fatalf("Expected 1 call to GCP.Put, got %d", len(mockGCP.PutRef))
	}
	if mockGCP.PutRef[0] != myName {
		t.Errorf("Expected write to %q, wrote to %q", myName, mockGCP.PutRef)
	}
	savedUser, isAdmin := unmarshalUser(t, mockGCP.PutContents[0])
	if !reflect.DeepEqual(*savedUser, *user) {
		t.Errorf("Expected Put to store User %v, but it stored %v", user, savedUser)
	}
	if !isAdmin {
		t.Error("Expected user to be an admin")
	}
}

// marshalUser marshals the user struct and whether the user is an admin into JSON bytes.
func marshalUser(t *testing.T, user *upspin.User, isAdmin bool) []byte {
	ue := userEntry{
		User:    *user,
		IsAdmin: isAdmin,
	}
	buf, err := json.Marshal(ue)
	if err != nil {
		t.Fatal(err)
	}
	return buf
}

// unmarshalUser unmarshals JSON bytes into the user struct, along with whether the user is an admin.
func unmarshalUser(t *testing.T, buf []byte) (*upspin.User, bool) {
	var ue userEntry
	err := json.Unmarshal(buf, &ue)
	if err != nil {
		t.Fatalf("Wrote invalid bytes: %q: %v", buf, err)
	}
	return &ue.User, ue.IsAdmin
}

// newDummyKeyServer creates a new keyserver.
func newDummyKeyServer() *key {
	return &key{cloudClient: &storagetest.DummyStorage{}}
}

// newKeyServerWithMocking sets up a mock GCP client for a user and expects a
// single lookup of user mockKey and it will reply with the preset
// data. It returns the user server, the mock GCP client for further
// verification.
func newKeyServerWithMocking(user upspin.UserName, ref string, data []byte) (*key, *storagetest.ExpectDownloadCapturePut) {
	mockGCP := &storagetest.ExpectDownloadCapturePut{
		Ref:         []string{ref},
		Data:        [][]byte{data},
		PutContents: make([][]byte, 0, 1),
		PutRef:      make([]string, 0, 1),
	}
	u := &key{
		cloudClient: mockGCP,
		context:     context.New().SetUserName(user),
	}
	return u, mockGCP
}
<commit_msg>key/gcp: use the right json package<commit_after>// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gcp

import (
	"encoding/json"
	"reflect"
	"strings"
	"testing"

	"upspin.io/cloud/storage/storagetest"
	"upspin.io/context"
	"upspin.io/upspin"
)

const isAdmin = true

func TestInvalidUser(t *testing.T) {
	u := newDummyKeyServer()
	_, err := u.Lookup("a")
	if err == nil {
		t.Fatal("Expected an error")
	}
	expected := "invalid user"
	if !strings.Contains(err.Error(), expected) {
		t.Errorf("Expected %q, got %q", expected, err)
	}
}

func TestPutOtherUserNotAdmin(t *testing.T) {
	const (
		myName    = "cool@dude.com"
		otherUser = "uncool@buddy.com"
	)

	// Pre-existing user: myName, who *is not* an admin.
	user := &upspin.User{
		Name: myName,
	}
	buf := marshalUser(t, user, !isAdmin)

	// Create a server authenticated with myName and with a pre-existing User entry for myName.
	u, mockGCP := newKeyServerWithMocking(myName, myName, buf)

	// myName now attempts to write somebody else's information.
	otherU := &upspin.User{
		Name:      otherUser,
		PublicKey: upspin.PublicKey("going to change your key, haha"),
	}
	err := u.Put(otherU)
	if err == nil {
		t.Fatal("Expected error, got none")
	}
	if !strings.Contains(err.Error(), "permission denied") {
		t.Errorf("Expected permission denied, got %q", err)
	}
	// Check that indeed we did not write to GCP.
	if len(mockGCP.PutRef) != 0 {
		t.Errorf("Expected no writes, got %d", len(mockGCP.PutRef))
	}
}

func TestPutOtherUserIsAdmin(t *testing.T) {
	const (
		myName    = "cool@dude.com"
		otherUser = "uncool@buddy.com"
	)

	// Pre-existing user: myName, who *is* an admin.
	user := &upspin.User{
		Name: myName,
	}
	buf := marshalUser(t, user, isAdmin)

	// Create a server authenticated with myName and with a pre-existing User entry for myName.
	u, mockGCP := newKeyServerWithMocking(myName, myName, buf)

	// myName now attempts to write somebody else's information.
	otherU := &upspin.User{
		Name:      otherUser,
		PublicKey: upspin.PublicKey("going to change your key, because I can"),
	}
	err := u.Put(otherU)
	if err != nil {
		t.Fatal("Expected no error")
	}
	// Check new user was written to GCP
	if len(mockGCP.PutRef) != 1 {
		t.Fatalf("Expected one write, got %d", len(mockGCP.PutRef))
	}
	if mockGCP.PutRef[0] != otherUser {
		t.Errorf("Expected write to user %q, wrote to %q", otherUser, mockGCP.PutRef[0])
	}
	savedUser, isAdmin := unmarshalUser(t, mockGCP.PutContents[0])
	if !reflect.DeepEqual(*savedUser, *otherU) {
		t.Errorf("Expected Put to store User %v, but it stored %v", otherU, savedUser)
	}
	if isAdmin {
		t.Error("Expected user not to be an admin")
	}
}

func TestPutNewUserSelf(t *testing.T) {
	const myName = "cool@dude.com"

	// New server for myName.
	u, mockGCP := newKeyServerWithMocking(myName, "", nil)

	user := &upspin.User{
		Name: myName,
		Dirs: []upspin.Endpoint{
			upspin.Endpoint{
				Transport: upspin.Remote,
				NetAddr:   upspin.NetAddr("there.co.uk"),
			},
		},
		Stores: []upspin.Endpoint{
			upspin.Endpoint{
				Transport: upspin.Remote,
				NetAddr:   upspin.NetAddr("down-under.au"),
			},
		},
		PublicKey: upspin.PublicKey("my key"),
	}
	err := u.Put(user)
	if err != nil {
		t.Fatal(err)
	}

	// Verify that GCP received the Put.
	if len(mockGCP.PutRef) != 1 || len(mockGCP.PutContents) != 1 {
		t.Fatalf("Expected 1 call to GCP.Put, got %d", len(mockGCP.PutRef))
	}
	if mockGCP.PutRef[0] != myName {
		t.Errorf("Expected write to %q, wrote to %q", myName, mockGCP.PutRef)
	}
	savedUser, isAdmin := unmarshalUser(t, mockGCP.PutContents[0])
	if !reflect.DeepEqual(*savedUser, *user) {
		t.Errorf("Expected Put to store User %v, but it stored %v", user, savedUser)
	}
	if isAdmin {
		t.Error("Expected user not to be an admin")
	}
}

func TestPutNewUserSelfIsAdmin(t *testing.T) {
	const myName = "cool@dude.com"

	user := &upspin.User{
		Name: myName,
		Stores: []upspin.Endpoint{
			upspin.Endpoint{
				Transport: upspin.Remote,
				NetAddr:   upspin.NetAddr("some.place:443"),
			},
		},
		PublicKey: upspin.PublicKey("super secure"),
	}
	buf := marshalUser(t, user, isAdmin)

	// New server for myName.
	u, mockGCP := newKeyServerWithMocking(myName, myName, buf)

	// Changing my user info to include a root dir.
	user.Dirs = append(user.Dirs, upspin.Endpoint{
		Transport: upspin.Remote,
		NetAddr:   upspin.NetAddr("my-root-dir:443"),
	})
	// Change my information.
	err := u.Put(user)
	if err != nil {
		t.Fatal(err)
	}

	// Verify that GCP received the Put.
	if len(mockGCP.PutRef) != 1 || len(mockGCP.PutContents) != 1 {
		t.Fatalf("Expected 1 call to GCP.Put, got %d", len(mockGCP.PutRef))
	}
	if mockGCP.PutRef[0] != myName {
		t.Errorf("Expected write to %q, wrote to %q", myName, mockGCP.PutRef)
	}
	savedUser, isAdmin := unmarshalUser(t, mockGCP.PutContents[0])
	if !reflect.DeepEqual(*savedUser, *user) {
		t.Errorf("Expected Put to store User %v, but it stored %v", user, savedUser)
	}
	if !isAdmin {
		t.Error("Expected user to be an admin")
	}
}

// marshalUser marshals the user struct and whether the user is an admin into JSON bytes.
func marshalUser(t *testing.T, user *upspin.User, isAdmin bool) []byte {
	ue := userEntry{
		User:    *user,
		IsAdmin: isAdmin,
	}
	buf, err := json.Marshal(ue)
	if err != nil {
		t.Fatal(err)
	}
	return buf
}

// unmarshalUser unmarshals JSON bytes into the user struct, along with whether the user is an admin.
func unmarshalUser(t *testing.T, buf []byte) (*upspin.User, bool) {
	var ue userEntry
	err := json.Unmarshal(buf, &ue)
	if err != nil {
		t.Fatalf("Wrote invalid bytes: %q: %v", buf, err)
	}
	return &ue.User, ue.IsAdmin
}

// newDummyKeyServer creates a new keyserver.
func newDummyKeyServer() *key {
	return &key{cloudClient: &storagetest.DummyStorage{}}
}

// newKeyServerWithMocking sets up a mock GCP client for a user and expects a
// single lookup of user mockKey and it will reply with the preset
// data. It returns the user server, the mock GCP client for further
// verification.
func newKeyServerWithMocking(user upspin.UserName, ref string, data []byte) (*key, *storagetest.ExpectDownloadCapturePut) {
	mockGCP := &storagetest.ExpectDownloadCapturePut{
		Ref:         []string{ref},
		Data:        [][]byte{data},
		PutContents: make([][]byte, 0, 1),
		PutRef:      make([]string, 0, 1),
	}
	u := &key{
		cloudClient: mockGCP,
		context:     context.New().SetUserName(user),
	}
	return u, mockGCP
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
// Program: fsimilar
// Purpose: find/file similar
// Authors: Tong Sun (c) 2017, All rights reserved
////////////////////////////////////////////////////////////////////////////

package main

import (
	"fmt"
	"io"
	"math/rand"
	"os"
	"path/filepath"
	"time"

	"github.com/mkideal/cli"
	"github.com/spakin/awk"
	"gopkg.in/pipe.v2"
	//clix "github.com/mkideal/cli/ext"
)

////////////////////////////////////////////////////////////////////////////
// Constant and data type/structure definitions

// The FileT type defines all the necessary info for a file.
type FileT struct {
	Dir  string
	Name string
	Ext  string
	Size int
	Hash uint64
	Dist uint8
}

//==========================================================================
// Main dispatcher

func fsimilar(ctx *cli.Context) error {
	ctx.JSON(ctx.RootArgv())
	ctx.JSON(ctx.Argv())
	fmt.Println()
	rootArgv = ctx.RootArgv().(*rootT)

	Opts.Distance, Opts.SizeGiven, Opts.Template, Opts.Verbose =
		rootArgv.Distance, rootArgv.SizeGiven, rootArgv.Template,
		rootArgv.Verbose.Value()

	return fSimilar(rootArgv.Filei)
}

func fSimilar(cin io.Reader) error {
	psrc, pdst := pipe.Script(
		pipe.Read(cin),
	), pipe.Script(
		pipe.Write(os.Stdout),
	)

	rand.Seed(time.Now().UTC().UnixNano())
	//tmpfile := fmt.Sprintf("%s.%d", file, 99999999-rand.Int31n(90000000))

	p := pipe.Line(
		psrc,
		scripttAwk(),
		pdst,
	)
	err := pipe.Run(p)
	abortOn("Pipe.Run", err)
	println("\nWrapping up.\n")

	return nil
}

func scripttAwk() pipe.Pipe {
	return pipe.TaskFunc(func(st *pipe.State) error {
		// == Setup
		s := awk.NewScript()
		s.Output = st.Stdout

		s.AppendStmt(nil, func(s *awk.Script) {
			file := FileT{}

			if Opts.SizeGiven {
				file.Size = s.F(1).Int()
				// $1 = ""
				// X: s.SetF(1, "")
			}
			p, n := filepath.Split(s.F(0).String())
			file.Dir, file.Name, file.Ext = p, Basename(n), filepath.Ext(n)
			fmt.Printf("  d='%s', n='%s', e='%s', s='%d'\n",
				file.Dir, file.Name, file.Ext, file.Size)
		})

		// 1; # i.e., print all
		//s.AppendStmt(nil, nil)

		// == Run it
		return s.Run(st.Stdin)
	})
}
<commit_msg>- [*] remove using awk, parsing works<commit_after>////////////////////////////////////////////////////////////////////////////
// Program: fsimilar
// Purpose: find/file similar
// Authors: Tong Sun (c) 2017, All rights reserved
////////////////////////////////////////////////////////////////////////////

package main

import (
	"bufio"
	"fmt"
	"io"
	"math/rand"
	"path/filepath"
	"regexp"
	"time"

	"github.com/mkideal/cli"
)

////////////////////////////////////////////////////////////////////////////
// Constant and data type/structure definitions

// The FileT type defines all the necessary info for a file.
type FileT struct {
	Dir  string
	Name string
	Ext  string
	Size int
	Hash uint64
	Dist uint8
}

//==========================================================================
// Main dispatcher

func fsimilar(ctx *cli.Context) error {
	ctx.JSON(ctx.RootArgv())
	ctx.JSON(ctx.Argv())
	fmt.Println()
	rootArgv = ctx.RootArgv().(*rootT)

	Opts.Distance, Opts.SizeGiven, Opts.Template, Opts.Verbose =
		rootArgv.Distance, rootArgv.SizeGiven, rootArgv.Template,
		rootArgv.Verbose.Value()

	return fSimilar(rootArgv.Filei)
}

func fSimilar(cin io.Reader) error {
	rand.Seed(time.Now().UTC().UnixNano())
	//tmpfile := fmt.Sprintf("%s.%d", file, 99999999-rand.Int31n(90000000))

	scanner := bufio.NewScanner(cin)
	for scanner.Scan() {
		file := FileT{}
		fn := scanner.Text()

		if Opts.SizeGiven {
			_, err := fmt.Sscan(fn, &file.Size)
			abortOn("Parsing file size", err)
			il := regexp.MustCompile(`^ *\d+ *(.*)$`).FindStringSubmatchIndex(fn)
			//fmt.Println(il)
			fn = fn[il[2]:]
		}
		p, n := filepath.Split(fn)
		file.Dir, file.Name, file.Ext = p, Basename(n), filepath.Ext(n)
		fmt.Printf("  d='%s', n='%s', e='%s', s='%d'\n",
			file.Dir, file.Name, file.Ext, file.Size)
	}

	return scanner.Err()
}
<|endoftext|>


<commit_before>package fsm

import (
	"fmt"
	"strconv"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/gen/swf"
)

// EventCorrelator is a serialization-friendly struct that is automatically managed by the FSM machinery
// It tracks signal and activity correlation info, so you know how to react when an event that signals the
// end of an activity or signal  hits your Decider.  This is missing from the SWF api.
// Activities and Signals are string instead of int64 beacuse json.
type EventCorrelator struct {
	Activities       map[string]*ActivityInfo //schedueledEventId -> info
	ActivityAttempts map[string]int           //activityID -> attempts
	Signals          map[string]*SignalInfo   //schedueledEventId -> info
	SignalAttempts   map[string]int           //? workflowID + signalName -> attempts
	Timers           map[string]*TimerInfo    //startedEventID -> info
}

// ActivityInfo holds the ActivityID and ActivityType for an activity
type ActivityInfo struct {
	ActivityID string
	*swf.ActivityType
}

// SignalInfo holds the SignalName and Input for an activity
type SignalInfo struct {
	SignalName string
	WorkflowID string
}

//TimerInfo holds the Control data from a Timer
type TimerInfo struct {
	Control string
	TimerID string
}

// Track will add or remove entries based on the EventType.
// A new entry is added when there is a new ActivityTask, or an entry is removed when the ActivityTask is terminating.
func (a *EventCorrelator) Track(h swf.HistoryEvent) {
	a.RemoveCorrelation(h)
	a.Correlate(h)
}

// Correlate establishes a mapping of eventId to ActivityType. The HistoryEvent is expected to be of type EventTypeActivityTaskScheduled.
func (a *EventCorrelator) Correlate(h swf.HistoryEvent) {
	a.checkInit()

	if a.nilSafeEq(h.EventType, swf.EventTypeActivityTaskScheduled) {

		a.Activities[a.key(h.EventID)] = &ActivityInfo{
			ActivityID:   *h.ActivityTaskScheduledEventAttributes.ActivityID,
			ActivityType: h.ActivityTaskScheduledEventAttributes.ActivityType,
		}
	}

	if a.nilSafeEq(h.EventType, swf.EventTypeSignalExternalWorkflowExecutionInitiated) {
		a.Signals[a.key(h.EventID)] = &SignalInfo{
			SignalName: *h.SignalExternalWorkflowExecutionInitiatedEventAttributes.SignalName,
			WorkflowID: *h.SignalExternalWorkflowExecutionInitiatedEventAttributes.WorkflowID,
		}
	}

	if a.nilSafeEq(h.EventType, swf.EventTypeTimerStarted) {
		control := ""
		if h.TimerStartedEventAttributes.Control != nil {
			control = *h.TimerStartedEventAttributes.Control
		}

		a.Timers[a.key(h.EventID)] = &TimerInfo{
			Control: control,
			TimerID: *h.TimerStartedEventAttributes.TimerID,
		}
	}
}

// RemoveCorrelation gcs a mapping of eventId to ActivityType. The HistoryEvent is expected to be of type EventTypeActivityTaskCompleted,EventTypeActivityTaskFailed,EventTypeActivityTaskTimedOut.
func (a *EventCorrelator) RemoveCorrelation(h swf.HistoryEvent) {
	a.checkInit()
	if h.EventType == nil {
		return
	}
	switch *h.EventType {
	case swf.EventTypeActivityTaskCompleted:
		delete(a.ActivityAttempts, a.safeActivityID(h))
		delete(a.Activities, a.key(h.ActivityTaskCompletedEventAttributes.ScheduledEventID))
	case swf.EventTypeActivityTaskFailed:
		a.incrementActivityAttempts(h)
		delete(a.Activities, a.key(h.ActivityTaskFailedEventAttributes.ScheduledEventID))
	case swf.EventTypeActivityTaskTimedOut:
		a.incrementActivityAttempts(h)
		delete(a.Activities, a.key(h.ActivityTaskTimedOutEventAttributes.ScheduledEventID))
	case swf.EventTypeActivityTaskCanceled:
		delete(a.ActivityAttempts, a.safeActivityID(h))
		delete(a.Activities, a.key(h.ActivityTaskCanceledEventAttributes.ScheduledEventID))
	case swf.EventTypeExternalWorkflowExecutionSignaled:
		key := a.key(h.ExternalWorkflowExecutionSignaledEventAttributes.InitiatedEventID)
		info := a.Signals[key]
		delete(a.SignalAttempts, a.signalIDFromInfo(info))
		delete(a.Signals, key)
	case swf.EventTypeSignalExternalWorkflowExecutionFailed:
		a.incrementSignalAttempts(h)
		delete(a.Signals, a.key(h.SignalExternalWorkflowExecutionFailedEventAttributes.InitiatedEventID))
	case swf.EventTypeTimerFired:
		delete(a.Timers, a.key(h.TimerFiredEventAttributes.StartedEventID))
	case swf.EventTypeTimerCanceled:
		delete(a.Timers, a.key(h.TimerCanceledEventAttributes.StartedEventID))
	}
}

// ActivityInfo returns the ActivityInfo that is correlates with a given event. The HistoryEvent is expected to be of type EventTypeActivityTaskCompleted,EventTypeActivityTaskFailed,EventTypeActivityTaskTimedOut.
func (a *EventCorrelator) ActivityInfo(h swf.HistoryEvent) *ActivityInfo {
	a.checkInit()
	return a.Activities[a.getID(h)]
}

// SignalInfo returns the SignalInfo that is correlates with a given event. The HistoryEvent is expected to be of type EventTypeSignalExternalWorkflowExecutionFailed,EventTypeExternalWorkflowExecutionSignaled.
func (a *EventCorrelator) SignalInfo(h swf.HistoryEvent) *SignalInfo {
	a.checkInit()
	return a.Signals[a.getID(h)]
}

func (a *EventCorrelator) TimerInfo(h swf.HistoryEvent) *TimerInfo {
	a.checkInit()
	return a.Timers[a.getID(h)]
}

//AttemptsForActivity returns the number of times a given activity has been attempted.
//It will return 0 if the activity has never failed, has been canceled, or has been completed successfully
func (a *EventCorrelator) AttemptsForActivity(info *ActivityInfo) int {
	a.checkInit()
	return a.ActivityAttempts[info.ActivityID]
}

//AttemptsForSignal returns the number of times a given signal has been attempted.
//It will return 0 if the signal has never failed, or has been completed successfully
func (a *EventCorrelator) AttemptsForSignal(signalInfo *SignalInfo) int {
	a.checkInit()
	return a.SignalAttempts[a.signalIDFromInfo(signalInfo)]
}

func (a *EventCorrelator) checkInit() {
	if a.Activities == nil {
		a.Activities = make(map[string]*ActivityInfo)
	}
	if a.ActivityAttempts == nil {
		a.ActivityAttempts = make(map[string]int)
	}
	if a.Signals == nil {
		a.Signals = make(map[string]*SignalInfo)
	}
	if a.SignalAttempts == nil {
		a.SignalAttempts = make(map[string]int)
	}
	if a.Timers == nil {
		a.Timers = make(map[string]*TimerInfo)
	}
}

func (a *EventCorrelator) getID(h swf.HistoryEvent) (id string) {
	switch *h.EventType {
	case swf.EventTypeActivityTaskCompleted:
		if h.ActivityTaskCompletedEventAttributes != nil {
			id = a.key(h.ActivityTaskCompletedEventAttributes.ScheduledEventID)
		}
	case swf.EventTypeActivityTaskFailed:
		if h.ActivityTaskFailedEventAttributes != nil {
			id = a.key(h.ActivityTaskFailedEventAttributes.ScheduledEventID)
		}
	case swf.EventTypeActivityTaskTimedOut:
		if h.ActivityTaskTimedOutEventAttributes != nil {
			id = a.key(h.ActivityTaskTimedOutEventAttributes.ScheduledEventID)
		}
	case swf.EventTypeActivityTaskCanceled:
		if h.ActivityTaskCanceledEventAttributes != nil {
			id = a.key(h.ActivityTaskCanceledEventAttributes.ScheduledEventID)
		}
	case swf.EventTypeExternalWorkflowExecutionSignaled:
		if h.ExternalWorkflowExecutionSignaledEventAttributes != nil {
			id = a.key(h.ExternalWorkflowExecutionSignaledEventAttributes.InitiatedEventID)
		}
	case swf.EventTypeSignalExternalWorkflowExecutionFailed:
		if h.SignalExternalWorkflowExecutionFailedEventAttributes != nil {
			id = a.key(h.SignalExternalWorkflowExecutionFailedEventAttributes.InitiatedEventID)
		}
	case swf.EventTypeTimerFired:
		if h.TimerFiredEventAttributes != nil {
			id = a.key(h.TimerFiredEventAttributes.StartedEventID)
		}
	case swf.EventTypeTimerCanceled:
		if h.TimerCanceledEventAttributes != nil {
			id = a.key(h.TimerCanceledEventAttributes.StartedEventID)
		}

	}
	return
}

func (a *EventCorrelator) safeActivityID(h swf.HistoryEvent) string {
	info := a.Activities[a.getID(h)]
	if info != nil {
		return info.ActivityID
	}
	return ""
}

func (a *EventCorrelator) safeSignalID(h swf.HistoryEvent) string {
	info := a.Signals[a.getID(h)]
	if info != nil {
		return a.signalIDFromInfo(info)
	}
	return ""
}

func (a *EventCorrelator) signalIDFromInfo(info *SignalInfo) string {
	return fmt.Sprintf("%s->%s", info.SignalName, info.WorkflowID)
}

func (a *EventCorrelator) incrementActivityAttempts(h swf.HistoryEvent) {
	id := a.safeActivityID(h)
	if id != "" {
		a.ActivityAttempts[id]++
	}
}

func (a *EventCorrelator) incrementSignalAttempts(h swf.HistoryEvent) {
	id := a.safeSignalID(h)
	if id != "" {
		a.SignalAttempts[id]++
	}
}

func (a *EventCorrelator) key(eventID aws.LongValue) string {
	return strconv.FormatInt(*eventID, 10)
}

func (a *EventCorrelator) nilSafeEq(sv aws.StringValue, s string) bool {
	if sv == nil {
		return false
	}

	return *sv == s
}
<commit_msg>get the StartedEventID from an ActivityStartedEvent if necessary<commit_after>package fsm

import (
	"fmt"
	"strconv"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/gen/swf"
)

// EventCorrelator is a serialization-friendly struct that is automatically managed by the FSM machinery
// It tracks signal and activity correlation info, so you know how to react when an event that signals the
// end of an activity or signal  hits your Decider.  This is missing from the SWF api.
// Activities and Signals are string instead of int64 beacuse json.
type EventCorrelator struct {
	Activities       map[string]*ActivityInfo //schedueledEventId -> info
	ActivityAttempts map[string]int           //activityID -> attempts
	Signals          map[string]*SignalInfo   //schedueledEventId -> info
	SignalAttempts   map[string]int           //? workflowID + signalName -> attempts
	Timers           map[string]*TimerInfo    //startedEventID -> info
}

// ActivityInfo holds the ActivityID and ActivityType for an activity
type ActivityInfo struct {
	ActivityID string
	*swf.ActivityType
}

// SignalInfo holds the SignalName and Input for an activity
type SignalInfo struct {
	SignalName string
	WorkflowID string
}

//TimerInfo holds the Control data from a Timer
type TimerInfo struct {
	Control string
	TimerID string
}

// Track will add or remove entries based on the EventType.
// A new entry is added when there is a new ActivityTask, or an entry is removed when the ActivityTask is terminating.
func (a *EventCorrelator) Track(h swf.HistoryEvent) {
	a.RemoveCorrelation(h)
	a.Correlate(h)
}

// Correlate establishes a mapping of eventId to ActivityType. The HistoryEvent is expected to be of type EventTypeActivityTaskScheduled.
func (a *EventCorrelator) Correlate(h swf.HistoryEvent) {
	a.checkInit()

	if a.nilSafeEq(h.EventType, swf.EventTypeActivityTaskScheduled) {

		a.Activities[a.key(h.EventID)] = &ActivityInfo{
			ActivityID:   *h.ActivityTaskScheduledEventAttributes.ActivityID,
			ActivityType: h.ActivityTaskScheduledEventAttributes.ActivityType,
		}
	}

	if a.nilSafeEq(h.EventType, swf.EventTypeSignalExternalWorkflowExecutionInitiated) {
		a.Signals[a.key(h.EventID)] = &SignalInfo{
			SignalName: *h.SignalExternalWorkflowExecutionInitiatedEventAttributes.SignalName,
			WorkflowID: *h.SignalExternalWorkflowExecutionInitiatedEventAttributes.WorkflowID,
		}
	}

	if a.nilSafeEq(h.EventType, swf.EventTypeTimerStarted) {
		control := ""
		if h.TimerStartedEventAttributes.Control != nil {
			control = *h.TimerStartedEventAttributes.Control
		}

		a.Timers[a.key(h.EventID)] = &TimerInfo{
			Control: control,
			TimerID: *h.TimerStartedEventAttributes.TimerID,
		}
	}
}

// RemoveCorrelation gcs a mapping of eventId to ActivityType. The HistoryEvent is expected to be of type EventTypeActivityTaskCompleted,EventTypeActivityTaskFailed,EventTypeActivityTaskTimedOut.
func (a *EventCorrelator) RemoveCorrelation(h swf.HistoryEvent) {
	a.checkInit()
	if h.EventType == nil {
		return
	}
	switch *h.EventType {
	case swf.EventTypeActivityTaskCompleted:
		delete(a.ActivityAttempts, a.safeActivityID(h))
		delete(a.Activities, a.key(h.ActivityTaskCompletedEventAttributes.ScheduledEventID))
	case swf.EventTypeActivityTaskFailed:
		a.incrementActivityAttempts(h)
		delete(a.Activities, a.key(h.ActivityTaskFailedEventAttributes.ScheduledEventID))
	case swf.EventTypeActivityTaskTimedOut:
		a.incrementActivityAttempts(h)
		delete(a.Activities, a.key(h.ActivityTaskTimedOutEventAttributes.ScheduledEventID))
	case swf.EventTypeActivityTaskCanceled:
		delete(a.ActivityAttempts, a.safeActivityID(h))
		delete(a.Activities, a.key(h.ActivityTaskCanceledEventAttributes.ScheduledEventID))
	case swf.EventTypeExternalWorkflowExecutionSignaled:
		key := a.key(h.ExternalWorkflowExecutionSignaledEventAttributes.InitiatedEventID)
		info := a.Signals[key]
		delete(a.SignalAttempts, a.signalIDFromInfo(info))
		delete(a.Signals, key)
	case swf.EventTypeSignalExternalWorkflowExecutionFailed:
		a.incrementSignalAttempts(h)
		delete(a.Signals, a.key(h.SignalExternalWorkflowExecutionFailedEventAttributes.InitiatedEventID))
	case swf.EventTypeTimerFired:
		delete(a.Timers, a.key(h.TimerFiredEventAttributes.StartedEventID))
	case swf.EventTypeTimerCanceled:
		delete(a.Timers, a.key(h.TimerCanceledEventAttributes.StartedEventID))
	}
}

// ActivityInfo returns the ActivityInfo that is correlates with a given event. The HistoryEvent is expected to be of type EventTypeActivityTaskCompleted,EventTypeActivityTaskFailed,EventTypeActivityTaskTimedOut.
func (a *EventCorrelator) ActivityInfo(h swf.HistoryEvent) *ActivityInfo {
	a.checkInit()
	return a.Activities[a.getID(h)]
}

// SignalInfo returns the SignalInfo that is correlates with a given event. The HistoryEvent is expected to be of type EventTypeSignalExternalWorkflowExecutionFailed,EventTypeExternalWorkflowExecutionSignaled.
func (a *EventCorrelator) SignalInfo(h swf.HistoryEvent) *SignalInfo {
	a.checkInit()
	return a.Signals[a.getID(h)]
}

func (a *EventCorrelator) TimerInfo(h swf.HistoryEvent) *TimerInfo {
	a.checkInit()
	return a.Timers[a.getID(h)]
}

//AttemptsForActivity returns the number of times a given activity has been attempted.
//It will return 0 if the activity has never failed, has been canceled, or has been completed successfully
func (a *EventCorrelator) AttemptsForActivity(info *ActivityInfo) int {
	a.checkInit()
	return a.ActivityAttempts[info.ActivityID]
}

//AttemptsForSignal returns the number of times a given signal has been attempted.
//It will return 0 if the signal has never failed, or has been completed successfully
func (a *EventCorrelator) AttemptsForSignal(signalInfo *SignalInfo) int {
	a.checkInit()
	return a.SignalAttempts[a.signalIDFromInfo(signalInfo)]
}

func (a *EventCorrelator) checkInit() {
	if a.Activities == nil {
		a.Activities = make(map[string]*ActivityInfo)
	}
	if a.ActivityAttempts == nil {
		a.ActivityAttempts = make(map[string]int)
	}
	if a.Signals == nil {
		a.Signals = make(map[string]*SignalInfo)
	}
	if a.SignalAttempts == nil {
		a.SignalAttempts = make(map[string]int)
	}
	if a.Timers == nil {
		a.Timers = make(map[string]*TimerInfo)
	}
}

func (a *EventCorrelator) getID(h swf.HistoryEvent) (id string) {
	switch *h.EventType {
	case swf.EventTypeActivityTaskCompleted:
		if h.ActivityTaskCompletedEventAttributes != nil {
			id = a.key(h.ActivityTaskCompletedEventAttributes.ScheduledEventID)
		}
	case swf.EventTypeActivityTaskFailed:
		if h.ActivityTaskFailedEventAttributes != nil {
			id = a.key(h.ActivityTaskFailedEventAttributes.ScheduledEventID)
		}
	case swf.EventTypeActivityTaskTimedOut:
		if h.ActivityTaskTimedOutEventAttributes != nil {
			id = a.key(h.ActivityTaskTimedOutEventAttributes.ScheduledEventID)
		}
	case swf.EventTypeActivityTaskCanceled:
		if h.ActivityTaskCanceledEventAttributes != nil {
			id = a.key(h.ActivityTaskCanceledEventAttributes.ScheduledEventID)
		}
	//might want to get info on started too
	case swf.EventTypeActivityTaskStarted:
		if h.ActivityTaskStartedEventAttributes != nil {
			id = a.key(h.ActivityTaskStartedEventAttributes.ScheduledEventID)
		}
	case swf.EventTypeExternalWorkflowExecutionSignaled:
		if h.ExternalWorkflowExecutionSignaledEventAttributes != nil {
			id = a.key(h.ExternalWorkflowExecutionSignaledEventAttributes.InitiatedEventID)
		}
	case swf.EventTypeSignalExternalWorkflowExecutionFailed:
		if h.SignalExternalWorkflowExecutionFailedEventAttributes != nil {
			id = a.key(h.SignalExternalWorkflowExecutionFailedEventAttributes.InitiatedEventID)
		}
	case swf.EventTypeTimerFired:
		if h.TimerFiredEventAttributes != nil {
			id = a.key(h.TimerFiredEventAttributes.StartedEventID)
		}
	case swf.EventTypeTimerCanceled:
		if h.TimerCanceledEventAttributes != nil {
			id = a.key(h.TimerCanceledEventAttributes.StartedEventID)
		}

	}
	return
}

func (a *EventCorrelator) safeActivityID(h swf.HistoryEvent) string {
	info := a.Activities[a.getID(h)]
	if info != nil {
		return info.ActivityID
	}
	return ""
}

func (a *EventCorrelator) safeSignalID(h swf.HistoryEvent) string {
	info := a.Signals[a.getID(h)]
	if info != nil {
		return a.signalIDFromInfo(info)
	}
	return ""
}

func (a *EventCorrelator) signalIDFromInfo(info *SignalInfo) string {
	return fmt.Sprintf("%s->%s", info.SignalName, info.WorkflowID)
}

func (a *EventCorrelator) incrementActivityAttempts(h swf.HistoryEvent) {
	id := a.safeActivityID(h)
	if id != "" {
		a.ActivityAttempts[id]++
	}
}

func (a *EventCorrelator) incrementSignalAttempts(h swf.HistoryEvent) {
	id := a.safeSignalID(h)
	if id != "" {
		a.SignalAttempts[id]++
	}
}

func (a *EventCorrelator) key(eventID aws.LongValue) string {
	return strconv.FormatInt(*eventID, 10)
}

func (a *EventCorrelator) nilSafeEq(sv aws.StringValue, s string) bool {
	if sv == nil {
		return false
	}

	return *sv == s
}
<|endoftext|>


<commit_before>// +build darwin

package keyring

import (
	"fmt"
	"log"

	gokeychain "github.com/keybase/go-keychain"
)

const (
	keychainAccessGroup = "ACE1234DEF.com.99designs.aws-vault"
)

type keychain struct {
	path       string
	service    string
	passphrase string
}

func init() {
	supportedBackends[KeychainBackend] = opener(func(name string) (Keyring, error) {
		if name == "" {
			name = "login"
		}

		return &keychain{
			service: name,
			path:    name + ".keychain",
		}, nil
	})

	DefaultBackend = KeychainBackend
}

func (k *keychain) Get(key string) (Item, error) {
	kc, err := k.createOrOpen()
	if err != nil {
		return Item{}, err
	}

	query := gokeychain.NewItem()
	query.SetSecClass(gokeychain.SecClassGenericPassword)
	query.SetService(k.service)
	query.SetAccount(key)
	query.SetMatchLimit(gokeychain.MatchLimitOne)
	query.SetReturnAttributes(true)
	query.SetReturnData(true)
	query.UseKeychain(kc)

	results, err := gokeychain.QueryItem(query)
	if err == gokeychain.ErrorItemNotFound || len(results) == 0 {
		return Item{}, ErrKeyNotFound
	}

	if err != nil {
		return Item{}, err
	}

	item := Item{
		Key:         key,
		Data:        results[0].Data,
		Label:       results[0].Label,
		Description: results[0].Description,
	}

	return item, nil
}

func (k *keychain) Set(item Item) error {
	kc, err := k.createOrOpen()
	if err != nil {
		return err
	}

	label := fmt.Sprintf("%s (%s)", k.service, item.Key)

	newItem := gokeychain.NewGenericPassword(k.service, item.Key, label, item.Data, keychainAccessGroup)
	newItem.UseKeychain(kc)
	newItem.SetAccess(gokeychain.NoApplicationsTrusted)

	log.Printf("Adding service=%q, account=%q to osx keychain %s", k.service, item.Key, k.path)
	return gokeychain.AddItem(newItem)
}

func (k *keychain) Remove(key string) error {
	kc := gokeychain.NewWithPath(k.path)

	if err := kc.Status(); err != nil {
		return err
	}

	item := gokeychain.NewItem()
	item.SetSecClass(gokeychain.SecClassGenericPassword)
	item.SetService(k.service)
	item.SetAccount(key)

	log.Printf("Removing keychain item service=%q, account=%q from osx keychain %q", k.service, key, k.path)
	return gokeychain.DeleteItem(item)
}

func (k *keychain) Keys() ([]string, error) {
	kc := gokeychain.NewWithPath(k.path)

	if err := kc.Status(); err != nil {
		return nil, err
	}

	query := gokeychain.NewItem()
	query.SetSecClass(gokeychain.SecClassGenericPassword)
	query.SetService(k.service)
	query.SetMatchLimit(gokeychain.MatchLimitAll)
	query.SetReturnAttributes(true)
	query.UseKeychain(kc)

	results, err := gokeychain.QueryItem(query)
	if err != nil {
		return nil, err
	}

	accountNames := make([]string, len(results))
	for idx, r := range results {
		accountNames[idx] = r.Account
	}

	return accountNames, nil
}

func (k *keychain) createOrOpen() (gokeychain.Keychain, error) {
	kc := gokeychain.NewWithPath(k.path)

	err := kc.Status()
	if err == nil {
		return kc, nil
	}

	if err != gokeychain.ErrorNoSuchKeychain {
		return gokeychain.Keychain{}, err
	}

	if k.passphrase != "" {
		log.Printf("Creating keychain %s with prompt", k.path)
		return gokeychain.NewKeychainWithPrompt(k.path)
	}

	log.Printf("Creating keychain %s with provided password", k.path)
	return gokeychain.NewKeychain(k.path, k.passphrase)
}
<commit_msg>Remove keychainAccessGroup<commit_after>// +build darwin

package keyring

import (
	"log"

	gokeychain "github.com/keybase/go-keychain"
)

type keychain struct {
	path       string
	service    string
	passphrase string
}

func init() {
	supportedBackends[KeychainBackend] = opener(func(name string) (Keyring, error) {
		if name == "" {
			name = "login"
		}

		return &keychain{
			service: name,
			path:    name + ".keychain",
		}, nil
	})

	DefaultBackend = KeychainBackend
}

func (k *keychain) Get(key string) (Item, error) {
	kc, err := k.createOrOpen()
	if err != nil {
		return Item{}, err
	}

	query := gokeychain.NewItem()
	query.SetSecClass(gokeychain.SecClassGenericPassword)
	query.SetService(k.service)
	query.SetAccount(key)
	query.SetMatchLimit(gokeychain.MatchLimitOne)
	query.SetReturnAttributes(true)
	query.SetReturnData(true)
	query.UseKeychain(kc)

	results, err := gokeychain.QueryItem(query)
	if err == gokeychain.ErrorItemNotFound || len(results) == 0 {
		return Item{}, ErrKeyNotFound
	}

	if err != nil {
		return Item{}, err
	}

	item := Item{
		Key:         key,
		Data:        results[0].Data,
		Label:       results[0].Label,
		Description: results[0].Description,
	}

	return item, nil
}

func (k *keychain) Set(item Item) error {
	kc, err := k.createOrOpen()
	if err != nil {
		return err
	}

	newItem := gokeychain.NewItem()
	newItem.SetSecClass(gokeychain.SecClassGenericPassword)
	newItem.SetService(k.service)
	newItem.SetAccount(item.Key)
	newItem.SetLabel(item.Label)
	newItem.SetDescription(item.Description)
	newItem.SetData(item.Data)
	newItem.SetSynchronizable(gokeychain.SynchronizableNo)
	newItem.SetAccessible(gokeychain.AccessibleWhenUnlocked)
	newItem.UseKeychain(kc)
	newItem.SetAccess(gokeychain.NoApplicationsTrusted)

	log.Printf("Adding service=%q, account=%q to osx keychain %s", k.service, item.Key, k.path)
	return gokeychain.AddItem(item)
}

func (k *keychain) Remove(key string) error {
	kc := gokeychain.NewWithPath(k.path)

	if err := kc.Status(); err != nil {
		return err
	}

	item := gokeychain.NewItem()
	item.SetSecClass(gokeychain.SecClassGenericPassword)
	item.SetService(k.service)
	item.SetAccount(key)

	log.Printf("Removing keychain item service=%q, account=%q from osx keychain %q", k.service, key, k.path)
	return gokeychain.DeleteItem(item)
}

func (k *keychain) Keys() ([]string, error) {
	kc := gokeychain.NewWithPath(k.path)

	if err := kc.Status(); err != nil {
		return nil, err
	}

	query := gokeychain.NewItem()
	query.SetSecClass(gokeychain.SecClassGenericPassword)
	query.SetService(k.service)
	query.SetMatchLimit(gokeychain.MatchLimitAll)
	query.SetReturnAttributes(true)
	query.UseKeychain(kc)

	results, err := gokeychain.QueryItem(query)
	if err != nil {
		return nil, err
	}

	accountNames := make([]string, len(results))
	for idx, r := range results {
		accountNames[idx] = r.Account
	}

	return accountNames, nil
}

func (k *keychain) createOrOpen() (gokeychain.Keychain, error) {
	kc := gokeychain.NewWithPath(k.path)

	err := kc.Status()
	if err == nil {
		return kc, nil
	}

	if err != gokeychain.ErrorNoSuchKeychain {
		return gokeychain.Keychain{}, err
	}

	if k.passphrase != "" {
		log.Printf("Creating keychain %s with prompt", k.path)
		return gokeychain.NewKeychainWithPrompt(k.path)
	}

	log.Printf("Creating keychain %s with provided password", k.path)
	return gokeychain.NewKeychain(k.path, k.passphrase)
}
<|endoftext|>


<commit_before>package influxdb

import (
	"compress/gzip"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/serializers/influx"
)

type APIErrorType int

const (
	_ APIErrorType = iota
	DatabaseNotFound
)

const (
	defaultRequestTimeout = time.Second * 5
	defaultDatabase       = "telegraf"
	defaultUserAgent      = "telegraf"

	errStringDatabaseNotFound      = "database not found"
	errStringHintedHandoffNotEmpty = "hinted handoff queue not empty"
	errStringPartialWrite          = "partial write"
	errStringPointsBeyondRP        = "points beyond retention policy"
	errStringUnableToParse         = "unable to parse"
)

var (

	// Escape an identifier in InfluxQL.
	escapeIdentifier = strings.NewReplacer(
		"\n", `\n`,
		`\`, `\\`,
		`"`, `\"`,
	)
)

// APIError is an error reported by the InfluxDB server
type APIError struct {
	StatusCode  int
	Title       string
	Description string
	Type        APIErrorType
}

func (e APIError) Error() string {
	if e.Description != "" {
		return fmt.Sprintf("%s: %s", e.Title, e.Description)
	}
	return e.Title
}

// QueryResponse is the response body from the /query endpoint
type QueryResponse struct {
	Results []QueryResult `json:"results"`
}

type QueryResult struct {
	Err string `json:"error,omitempty"`
}

func (r QueryResponse) Error() string {
	if len(r.Results) > 0 {
		return r.Results[0].Err
	}
	return ""
}

// WriteResponse is the response body from the /write endpoint
type WriteResponse struct {
	Err string `json:"error,omitempty"`
}

func (r WriteResponse) Error() string {
	return r.Err
}

type HTTPConfig struct {
	URL             *url.URL
	UserAgent       string
	Timeout         time.Duration
	Username        string
	Password        string
	TLSConfig       *tls.Config
	Proxy           *url.URL
	Headers         map[string]string
	ContentEncoding string
	Database        string
	RetentionPolicy string
	Consistency     string

	InfluxUintSupport bool `toml:"influx_uint_support"`
	Serializer        *influx.Serializer
}

type httpClient struct {
	WriteURL        string
	QueryURL        string
	ContentEncoding string
	Timeout         time.Duration
	Username        string
	Password        string
	Headers         map[string]string

	client     *http.Client
	serializer *influx.Serializer
	url        *url.URL
	database   string
}

func NewHTTPClient(config *HTTPConfig) (*httpClient, error) {
	if config.URL == nil {
		return nil, ErrMissingURL
	}

	database := config.Database
	if database == "" {
		database = defaultDatabase
	}

	timeout := config.Timeout
	if timeout == 0 {
		timeout = defaultRequestTimeout
	}

	userAgent := config.UserAgent
	if userAgent == "" {
		userAgent = defaultUserAgent
	}

	var headers = make(map[string]string, len(config.Headers)+1)
	headers["User-Agent"] = userAgent
	for k, v := range config.Headers {
		headers[k] = v
	}

	var proxy func(*http.Request) (*url.URL, error)
	if config.Proxy != nil {
		proxy = http.ProxyURL(config.Proxy)
	} else {
		proxy = http.ProxyFromEnvironment
	}

	serializer := config.Serializer
	if serializer == nil {
		serializer = influx.NewSerializer()
	}

	writeURL := makeWriteURL(
		config.URL,
		database,
		config.RetentionPolicy,
		config.Consistency)
	queryURL := makeQueryURL(config.URL)

	var transport *http.Transport
	switch config.URL.Scheme {
	case "http", "https":
		transport = &http.Transport{
			Proxy:           proxy,
			TLSClientConfig: config.TLSConfig,
		}
	case "unix":
		transport = &http.Transport{
			Dial: func(_, _ string) (net.Conn, error) {
				return net.DialTimeout(
					config.URL.Scheme,
					config.URL.Path,
					defaultRequestTimeout,
				)
			},
		}
	default:
		return nil, fmt.Errorf("unsupported scheme %q", config.URL.Scheme)
	}

	client := &httpClient{
		serializer: serializer,
		client: &http.Client{
			Timeout:   timeout,
			Transport: transport,
		},
		database:        database,
		url:             config.URL,
		WriteURL:        writeURL,
		QueryURL:        queryURL,
		ContentEncoding: config.ContentEncoding,
		Timeout:         timeout,
		Username:        config.Username,
		Password:        config.Password,
		Headers:         headers,
	}
	return client, nil
}

// URL returns the origin URL that this client connects too.
func (c *httpClient) URL() string {
	return c.url.String()
}

// URL returns the database that this client connects too.
func (c *httpClient) Database() string {
	return c.database
}

// CreateDatabase attemps to create a new database in the InfluxDB server.
// Note that some names are not allowed by the server, notably those with
// non-printable characters or slashes.
func (c *httpClient) CreateDatabase(ctx context.Context) error {
	query := fmt.Sprintf(`CREATE DATABASE "%s"`,
		escapeIdentifier.Replace(c.database))

	req, err := c.makeQueryRequest(query)

	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	queryResp := &QueryResponse{}
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(queryResp)

	if err != nil {
		if resp.StatusCode == 200 {
			return nil
		}

		return &APIError{
			StatusCode: resp.StatusCode,
			Title:      resp.Status,
		}
	}

	// Even with a 200 response there can be an error
	if resp.StatusCode == http.StatusOK && queryResp.Error() == "" {
		return nil
	}

	return &APIError{
		StatusCode:  resp.StatusCode,
		Title:       resp.Status,
		Description: queryResp.Error(),
	}
}

// Write sends the metrics to InfluxDB
func (c *httpClient) Write(ctx context.Context, metrics []telegraf.Metric) error {
	var err error

	reader := influx.NewReader(metrics, c.serializer)
	req, err := c.makeWriteRequest(reader)
	if err != nil {
		return err
	}

	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusNoContent {
		return nil
	}

	writeResp := &WriteResponse{}
	dec := json.NewDecoder(resp.Body)

	var desc string
	err = dec.Decode(writeResp)
	if err == nil {
		desc = writeResp.Err
	}

	if strings.Contains(desc, errStringDatabaseNotFound) {
		return &APIError{
			StatusCode:  resp.StatusCode,
			Title:       resp.Status,
			Description: desc,
			Type:        DatabaseNotFound,
		}
	}

	// This "error" is an informational message about the state of the
	// InfluxDB cluster.
	if strings.Contains(desc, errStringHintedHandoffNotEmpty) {
		return nil
	}

	// Points beyond retention policy is returned when points are immediately
	// discarded for being older than the retention policy.  Usually this not
	// a cause for concern and we don't want to retry.
	if strings.Contains(desc, errStringPointsBeyondRP) {
		log.Printf("W! [outputs.influxdb]: when writing to [%s]: received error %v",
			c.URL(), desc)
		return nil
	}

	// Other partial write errors, such as "field type conflict", are not
	// correctable at this point and so the point is dropped instead of
	// retrying.
	if strings.Contains(desc, errStringPartialWrite) {
		log.Printf("E! [outputs.influxdb]: when writing to [%s]: received error %v; discarding points",
			c.URL(), desc)
		return nil
	}

	// This error indicates a bug in either Telegraf line protocol
	// serialization, retries would not be successful.
	if strings.Contains(desc, errStringUnableToParse) {
		log.Printf("E! [outputs.influxdb]: when writing to [%s]: received error %v; discarding points",
			c.URL(), desc)
		return nil
	}

	return &APIError{
		StatusCode:  resp.StatusCode,
		Title:       resp.Status,
		Description: desc,
	}
}

func (c *httpClient) makeQueryRequest(query string) (*http.Request, error) {
	params := url.Values{}
	params.Set("q", query)
	form := strings.NewReader(params.Encode())

	req, err := http.NewRequest("POST", c.QueryURL, form)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.addHeaders(req)

	return req, nil
}

func (c *httpClient) makeWriteRequest(body io.Reader) (*http.Request, error) {
	var err error
	if c.ContentEncoding == "gzip" {
		body, err = compressWithGzip(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest("POST", c.WriteURL, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "text/plain; charset=utf-8")
	c.addHeaders(req)

	if c.ContentEncoding == "gzip" {
		req.Header.Set("Content-Encoding", "gzip")
	}

	return req, nil
}

func compressWithGzip(data io.Reader) (io.Reader, error) {
	pr, pw := io.Pipe()
	gw := gzip.NewWriter(pw)
	var err error

	go func() {
		_, err = io.Copy(gw, data)
		gw.Close()
		pw.Close()
	}()

	return pr, err
}

func (c *httpClient) addHeaders(req *http.Request) {
	if c.Username != "" || c.Password != "" {
		req.SetBasicAuth(c.Username, c.Password)
	}

	for header, value := range c.Headers {
		req.Header.Set(header, value)
	}
}

func makeWriteURL(loc *url.URL, db, rp, consistency string) string {
	params := url.Values{}
	params.Set("db", db)

	if rp != "" {
		params.Set("rp", rp)
	}

	if consistency != "one" && consistency != "" {
		params.Set("consistency", consistency)
	}

	u := *loc
	switch u.Scheme {
	case "unix":
		u.Scheme = "http"
		u.Host = "127.0.0.1"
		u.Path = "/write"
	case "http":
		u.Path = path.Join(u.Path, "write")
	}
	u.RawQuery = params.Encode()
	return u.String()
}

func makeQueryURL(loc *url.URL) string {
	u := *loc
	switch u.Scheme {
	case "unix":
		u.Scheme = "http"
		u.Host = "127.0.0.1"
		u.Path = "/query"
	case "http":
		u.Path = path.Join(u.Path, "query")
	}
	return u.String()
}
<commit_msg>Fix https in InfluxDB output (#3976)<commit_after>package influxdb

import (
	"compress/gzip"
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/serializers/influx"
)

type APIErrorType int

const (
	_ APIErrorType = iota
	DatabaseNotFound
)

const (
	defaultRequestTimeout = time.Second * 5
	defaultDatabase       = "telegraf"
	defaultUserAgent      = "telegraf"

	errStringDatabaseNotFound      = "database not found"
	errStringHintedHandoffNotEmpty = "hinted handoff queue not empty"
	errStringPartialWrite          = "partial write"
	errStringPointsBeyondRP        = "points beyond retention policy"
	errStringUnableToParse         = "unable to parse"
)

var (

	// Escape an identifier in InfluxQL.
	escapeIdentifier = strings.NewReplacer(
		"\n", `\n`,
		`\`, `\\`,
		`"`, `\"`,
	)
)

// APIError is an error reported by the InfluxDB server
type APIError struct {
	StatusCode  int
	Title       string
	Description string
	Type        APIErrorType
}

func (e APIError) Error() string {
	if e.Description != "" {
		return fmt.Sprintf("%s: %s", e.Title, e.Description)
	}
	return e.Title
}

// QueryResponse is the response body from the /query endpoint
type QueryResponse struct {
	Results []QueryResult `json:"results"`
}

type QueryResult struct {
	Err string `json:"error,omitempty"`
}

func (r QueryResponse) Error() string {
	if len(r.Results) > 0 {
		return r.Results[0].Err
	}
	return ""
}

// WriteResponse is the response body from the /write endpoint
type WriteResponse struct {
	Err string `json:"error,omitempty"`
}

func (r WriteResponse) Error() string {
	return r.Err
}

type HTTPConfig struct {
	URL             *url.URL
	UserAgent       string
	Timeout         time.Duration
	Username        string
	Password        string
	TLSConfig       *tls.Config
	Proxy           *url.URL
	Headers         map[string]string
	ContentEncoding string
	Database        string
	RetentionPolicy string
	Consistency     string

	InfluxUintSupport bool `toml:"influx_uint_support"`
	Serializer        *influx.Serializer
}

type httpClient struct {
	WriteURL        string
	QueryURL        string
	ContentEncoding string
	Timeout         time.Duration
	Username        string
	Password        string
	Headers         map[string]string

	client     *http.Client
	serializer *influx.Serializer
	url        *url.URL
	database   string
}

func NewHTTPClient(config *HTTPConfig) (*httpClient, error) {
	if config.URL == nil {
		return nil, ErrMissingURL
	}

	database := config.Database
	if database == "" {
		database = defaultDatabase
	}

	timeout := config.Timeout
	if timeout == 0 {
		timeout = defaultRequestTimeout
	}

	userAgent := config.UserAgent
	if userAgent == "" {
		userAgent = defaultUserAgent
	}

	var headers = make(map[string]string, len(config.Headers)+1)
	headers["User-Agent"] = userAgent
	for k, v := range config.Headers {
		headers[k] = v
	}

	var proxy func(*http.Request) (*url.URL, error)
	if config.Proxy != nil {
		proxy = http.ProxyURL(config.Proxy)
	} else {
		proxy = http.ProxyFromEnvironment
	}

	serializer := config.Serializer
	if serializer == nil {
		serializer = influx.NewSerializer()
	}

	writeURL := makeWriteURL(
		config.URL,
		database,
		config.RetentionPolicy,
		config.Consistency)
	queryURL := makeQueryURL(config.URL)

	var transport *http.Transport
	switch config.URL.Scheme {
	case "http", "https":
		transport = &http.Transport{
			Proxy:           proxy,
			TLSClientConfig: config.TLSConfig,
		}
	case "unix":
		transport = &http.Transport{
			Dial: func(_, _ string) (net.Conn, error) {
				return net.DialTimeout(
					config.URL.Scheme,
					config.URL.Path,
					defaultRequestTimeout,
				)
			},
		}
	default:
		return nil, fmt.Errorf("unsupported scheme %q", config.URL.Scheme)
	}

	client := &httpClient{
		serializer: serializer,
		client: &http.Client{
			Timeout:   timeout,
			Transport: transport,
		},
		database:        database,
		url:             config.URL,
		WriteURL:        writeURL,
		QueryURL:        queryURL,
		ContentEncoding: config.ContentEncoding,
		Timeout:         timeout,
		Username:        config.Username,
		Password:        config.Password,
		Headers:         headers,
	}
	return client, nil
}

// URL returns the origin URL that this client connects too.
func (c *httpClient) URL() string {
	return c.url.String()
}

// URL returns the database that this client connects too.
func (c *httpClient) Database() string {
	return c.database
}

// CreateDatabase attemps to create a new database in the InfluxDB server.
// Note that some names are not allowed by the server, notably those with
// non-printable characters or slashes.
func (c *httpClient) CreateDatabase(ctx context.Context) error {
	query := fmt.Sprintf(`CREATE DATABASE "%s"`,
		escapeIdentifier.Replace(c.database))

	req, err := c.makeQueryRequest(query)

	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	queryResp := &QueryResponse{}
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(queryResp)

	if err != nil {
		if resp.StatusCode == 200 {
			return nil
		}

		return &APIError{
			StatusCode: resp.StatusCode,
			Title:      resp.Status,
		}
	}

	// Even with a 200 response there can be an error
	if resp.StatusCode == http.StatusOK && queryResp.Error() == "" {
		return nil
	}

	return &APIError{
		StatusCode:  resp.StatusCode,
		Title:       resp.Status,
		Description: queryResp.Error(),
	}
}

// Write sends the metrics to InfluxDB
func (c *httpClient) Write(ctx context.Context, metrics []telegraf.Metric) error {
	var err error

	reader := influx.NewReader(metrics, c.serializer)
	req, err := c.makeWriteRequest(reader)
	if err != nil {
		return err
	}

	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusNoContent {
		return nil
	}

	writeResp := &WriteResponse{}
	dec := json.NewDecoder(resp.Body)

	var desc string
	err = dec.Decode(writeResp)
	if err == nil {
		desc = writeResp.Err
	}

	if strings.Contains(desc, errStringDatabaseNotFound) {
		return &APIError{
			StatusCode:  resp.StatusCode,
			Title:       resp.Status,
			Description: desc,
			Type:        DatabaseNotFound,
		}
	}

	// This "error" is an informational message about the state of the
	// InfluxDB cluster.
	if strings.Contains(desc, errStringHintedHandoffNotEmpty) {
		return nil
	}

	// Points beyond retention policy is returned when points are immediately
	// discarded for being older than the retention policy.  Usually this not
	// a cause for concern and we don't want to retry.
	if strings.Contains(desc, errStringPointsBeyondRP) {
		log.Printf("W! [outputs.influxdb]: when writing to [%s]: received error %v",
			c.URL(), desc)
		return nil
	}

	// Other partial write errors, such as "field type conflict", are not
	// correctable at this point and so the point is dropped instead of
	// retrying.
	if strings.Contains(desc, errStringPartialWrite) {
		log.Printf("E! [outputs.influxdb]: when writing to [%s]: received error %v; discarding points",
			c.URL(), desc)
		return nil
	}

	// This error indicates a bug in either Telegraf line protocol
	// serialization, retries would not be successful.
	if strings.Contains(desc, errStringUnableToParse) {
		log.Printf("E! [outputs.influxdb]: when writing to [%s]: received error %v; discarding points",
			c.URL(), desc)
		return nil
	}

	return &APIError{
		StatusCode:  resp.StatusCode,
		Title:       resp.Status,
		Description: desc,
	}
}

func (c *httpClient) makeQueryRequest(query string) (*http.Request, error) {
	params := url.Values{}
	params.Set("q", query)
	form := strings.NewReader(params.Encode())

	req, err := http.NewRequest("POST", c.QueryURL, form)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.addHeaders(req)

	return req, nil
}

func (c *httpClient) makeWriteRequest(body io.Reader) (*http.Request, error) {
	var err error
	if c.ContentEncoding == "gzip" {
		body, err = compressWithGzip(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest("POST", c.WriteURL, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "text/plain; charset=utf-8")
	c.addHeaders(req)

	if c.ContentEncoding == "gzip" {
		req.Header.Set("Content-Encoding", "gzip")
	}

	return req, nil
}

func compressWithGzip(data io.Reader) (io.Reader, error) {
	pr, pw := io.Pipe()
	gw := gzip.NewWriter(pw)
	var err error

	go func() {
		_, err = io.Copy(gw, data)
		gw.Close()
		pw.Close()
	}()

	return pr, err
}

func (c *httpClient) addHeaders(req *http.Request) {
	if c.Username != "" || c.Password != "" {
		req.SetBasicAuth(c.Username, c.Password)
	}

	for header, value := range c.Headers {
		req.Header.Set(header, value)
	}
}

func makeWriteURL(loc *url.URL, db, rp, consistency string) string {
	params := url.Values{}
	params.Set("db", db)

	if rp != "" {
		params.Set("rp", rp)
	}

	if consistency != "one" && consistency != "" {
		params.Set("consistency", consistency)
	}

	u := *loc
	switch u.Scheme {
	case "unix":
		u.Scheme = "http"
		u.Host = "127.0.0.1"
		u.Path = "/write"
	case "http":
	case "https":
		u.Path = path.Join(u.Path, "write")
	}
	u.RawQuery = params.Encode()
	return u.String()
}

func makeQueryURL(loc *url.URL) string {
	u := *loc
	switch u.Scheme {
	case "unix":
		u.Scheme = "http"
		u.Host = "127.0.0.1"
		u.Path = "/query"
	case "http":
	case "https":
		u.Path = path.Join(u.Path, "query")
	}
	return u.String()
}
<|endoftext|>


<commit_before>// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gdb

import (
	"database/sql"
	"fmt"
	"reflect"

	"github.com/gogf/gf/errors/gerror"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/text/gstr"
	"github.com/gogf/gf/util/gconv"
	"github.com/gogf/gf/util/gutil"
)

// Update does "UPDATE ... " statement for the model.
//
// If the optional parameter `dataAndWhere` is given, the dataAndWhere[0] is the updated data field,
// and dataAndWhere[1:] is treated as where condition fields.
// Also see Model.Data and Model.Where functions.
func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err error) {
	if len(dataAndWhere) > 0 {
		if len(dataAndWhere) > 2 {
			return m.Data(dataAndWhere[0]).Where(dataAndWhere[1], dataAndWhere[2:]...).Update()
		} else if len(dataAndWhere) == 2 {
			return m.Data(dataAndWhere[0]).Where(dataAndWhere[1]).Update()
		} else {
			return m.Data(dataAndWhere[0]).Update()
		}
	}
	defer func() {
		if err == nil {
			m.checkAndRemoveCache()
		}
	}()
	if m.data == nil {
		return nil, gerror.New("updating table with empty data")
	}
	var (
		updateData                                    = m.data
		fieldNameCreate                               = m.getSoftFieldNameCreated()
		fieldNameUpdate                               = m.getSoftFieldNameUpdated()
		fieldNameDelete                               = m.getSoftFieldNameDeleted()
		conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, false)
	)
	// Automatically update the record updating time.
	if !m.unscoped && fieldNameUpdate != "" {
		var (
			refValue = reflect.ValueOf(m.data)
			refKind  = refValue.Kind()
		)
		if refKind == reflect.Ptr {
			refValue = refValue.Elem()
			refKind = refValue.Kind()
		}
		switch refKind {
		case reflect.Map, reflect.Struct:
			dataMap := ConvertDataForTableRecord(m.data)
			gutil.MapDelete(dataMap, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
			if fieldNameUpdate != "" {
				dataMap[fieldNameUpdate] = gtime.Now().String()
			}
			updateData = dataMap
		default:
			updates := gconv.String(m.data)
			if fieldNameUpdate != "" && !gstr.Contains(updates, fieldNameUpdate) {
				updates += fmt.Sprintf(`,%s='%s'`, fieldNameUpdate, gtime.Now().String())
			}
			updateData = updates
		}
	}
	newData, err := m.filterDataForInsertOrUpdate(updateData)
	if err != nil {
		return nil, err
	}
	conditionStr := conditionWhere + conditionExtra
	if !gstr.ContainsI(conditionStr, " WHERE ") {
		return nil, gerror.New("there should be WHERE condition statement for UPDATE operation")
	}
	return m.db.DoUpdate(
		m.GetCtx(),
		m.getLink(true),
		m.tables,
		newData,
		conditionStr,
		m.mergeArguments(conditionArgs)...,
	)
}

// Increment increments a column's value by a given amount.
func (m *Model) Increment(column string, amount float64) (sql.Result, error) {
	return m.getModel().Data(column, &Counter{
		Field: column,
		Value: amount,
	}).Update()
}

// Decrement decrements a column's value by a given amount.
func (m *Model) Decrement(column string, amount float64) (sql.Result, error) {
	return m.getModel().Data(column, &Counter{
		Field: column,
		Value: -amount,
	}).Update()
}
<commit_msg>improve function Increment/Decrement for package gdb<commit_after>// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gdb

import (
	"database/sql"
	"fmt"
	"reflect"

	"github.com/gogf/gf/errors/gerror"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/text/gstr"
	"github.com/gogf/gf/util/gconv"
	"github.com/gogf/gf/util/gutil"
)

// Update does "UPDATE ... " statement for the model.
//
// If the optional parameter `dataAndWhere` is given, the dataAndWhere[0] is the updated data field,
// and dataAndWhere[1:] is treated as where condition fields.
// Also see Model.Data and Model.Where functions.
func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err error) {
	if len(dataAndWhere) > 0 {
		if len(dataAndWhere) > 2 {
			return m.Data(dataAndWhere[0]).Where(dataAndWhere[1], dataAndWhere[2:]...).Update()
		} else if len(dataAndWhere) == 2 {
			return m.Data(dataAndWhere[0]).Where(dataAndWhere[1]).Update()
		} else {
			return m.Data(dataAndWhere[0]).Update()
		}
	}
	defer func() {
		if err == nil {
			m.checkAndRemoveCache()
		}
	}()
	if m.data == nil {
		return nil, gerror.New("updating table with empty data")
	}
	var (
		updateData                                    = m.data
		fieldNameCreate                               = m.getSoftFieldNameCreated()
		fieldNameUpdate                               = m.getSoftFieldNameUpdated()
		fieldNameDelete                               = m.getSoftFieldNameDeleted()
		conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, false)
	)
	// Automatically update the record updating time.
	if !m.unscoped && fieldNameUpdate != "" {
		var (
			refValue = reflect.ValueOf(m.data)
			refKind  = refValue.Kind()
		)
		if refKind == reflect.Ptr {
			refValue = refValue.Elem()
			refKind = refValue.Kind()
		}
		switch refKind {
		case reflect.Map, reflect.Struct:
			dataMap := ConvertDataForTableRecord(m.data)
			gutil.MapDelete(dataMap, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
			if fieldNameUpdate != "" {
				dataMap[fieldNameUpdate] = gtime.Now().String()
			}
			updateData = dataMap
		default:
			updates := gconv.String(m.data)
			if fieldNameUpdate != "" && !gstr.Contains(updates, fieldNameUpdate) {
				updates += fmt.Sprintf(`,%s='%s'`, fieldNameUpdate, gtime.Now().String())
			}
			updateData = updates
		}
	}
	newData, err := m.filterDataForInsertOrUpdate(updateData)
	if err != nil {
		return nil, err
	}
	conditionStr := conditionWhere + conditionExtra
	if !gstr.ContainsI(conditionStr, " WHERE ") {
		return nil, gerror.New("there should be WHERE condition statement for UPDATE operation")
	}
	return m.db.DoUpdate(
		m.GetCtx(),
		m.getLink(true),
		m.tables,
		newData,
		conditionStr,
		m.mergeArguments(conditionArgs)...,
	)
}

// Increment increments a column's value by a given amount.
// The parameter `amount` can be type of float or integer.
func (m *Model) Increment(column string, amount interface{}) (sql.Result, error) {
	return m.getModel().Data(column, &Counter{
		Field: column,
		Value: gconv.Float64(amount),
	}).Update()
}

// Decrement decrements a column's value by a given amount.
// The parameter `amount` can be type of float or integer.
func (m *Model) Decrement(column string, amount interface{}) (sql.Result, error) {
	return m.getModel().Data(column, &Counter{
		Field: column,
		Value: -gconv.Float64(amount),
	}).Update()
}
<|endoftext|>


<commit_before>package controller

import (
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/oinume/lekcije/server/context_data"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestIndex(t *testing.T) {
	t.Parallel()
	a := assert.New(t)
	r := require.New(t)
	err := os.Chdir("../..")
	r.NoError(err)

	server := httptest.NewServer(http.HandlerFunc(Index))
	defer server.Close()

	req, err := http.NewRequest("GET", server.URL, nil)
	r.NoError(err)
	ctx := context_data.SetTrackingID(req.Context(), "a")
	req = req.WithContext(ctx)

	rr := httptest.NewRecorder()
	http.HandlerFunc(Index).ServeHTTP(rr, req)
	a.Equal(http.StatusOK, rr.Code)
	a.Contains(rr.Body.String(), "<title>lekcije - DMM英会話のお気に入り講師をフォローしよう</title>")
}
<commit_msg>Remove creating a server<commit_after>package controller

import (
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/oinume/lekcije/server/context_data"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestIndex(t *testing.T) {
	t.Parallel()
	a := assert.New(t)
	r := require.New(t)
	err := os.Chdir("../..")
	r.NoError(err)

	req, err := http.NewRequest("GET", "/", nil)
	r.NoError(err)
	ctx := context_data.SetTrackingID(req.Context(), "a")
	req = req.WithContext(ctx)

	rr := httptest.NewRecorder()
	http.HandlerFunc(Index).ServeHTTP(rr, req)
	a.Equal(http.StatusOK, rr.Code)
	a.Contains(rr.Body.String(), "<title>lekcije - DMM英会話のお気に入り講師をフォローしよう</title>")
}
<|endoftext|>


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

package main

import (
	"fmt"
	"net/http"
	"regexp"
	"strconv"
	"strings"
)

var crontab_id = regexp.MustCompile(`^/([0-9]+)$`)

/* TaskCreateHandler creates new tasks. It accepts the following arguments:
- cron:		Cron string to schedule the task [required]
- src[url]:	URL that's hit when cron executes [required]
- src[method]:	HTTP method used for accessing the source URL [default: GET]
- src[body]:	Optional POST payload for the source URL [default: empty]
- src[policy]:	Policy for accessing the source URL [default: once]
- dst[url]:	URL that'll receive the task response [required]
- dst[policy]:	Policy for accessing the destination URL [default: once]

Supported policies are:
- once:		Try once and give up if it fails [default]
- persist:	Retry for ever until it works
- retry,N:	Use N as the number of times to retry before giving up
*/
func TaskCreateHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.Header().Set("Allow", "POST")
		http.Error(w, http.StatusText(405), 405)
		return
	}
	cron := r.FormValue("cron")
	tsrc := TaskSrc{
		URL:    r.FormValue("src[url]"),
		Method: FormValueDefault(r, "src[method]", "GET"),
		Body:   r.FormValue("src[body]"),
		Policy: FormValueDefault(r, "src[policy]", "once"),
	}
	tdst := TaskDst{
		URL:    r.FormValue("dst[url]"),
		Policy: FormValueDefault(r, "dst[policy]", "once"),
	}
	var errmsg string
	switch {
	case cron == "":
		errmsg = "Missing cron"
	case tsrc.URL == "":
		errmsg = "Missing src[url]"
	case !ValidMethod(tsrc.Method):
		errmsg = "Invalid src[method]"
	case !ValidPolicy(tsrc.Policy):
		errmsg = "Invalid src[policy]"
	case tsrc.Method == "GET" && tsrc.Body != "":
		errmsg = "Invalid src[method]=GET with non-empty src[body]"
	case tsrc.Method == "POST" && tsrc.Body == "":
		errmsg = "Invalid src[method]=POST with empty src[body]"
	case tdst.URL == "":
		errmsg = "Missing dst[url]"
	case !ValidPolicy(tdst.Policy):
		errmsg = "Invalid dst[policy]"
	}
	if errmsg != "" {
		http.Error(w, errmsg, 400)
		return
	}
	t, _ := NewTask(cron, tsrc, tdst)
	fmt.Println(t)
	fmt.Fprintf(w, "OK\r\n")
}

func ValidMethod(s string) bool {
	switch s {
	case "DELETE":
	case "GET":
	case "POST":
	case "PUT":
	default:
		return false
	}
	return true
}

func ValidPolicy(s string) bool {
	tmp := strings.SplitN(s, ",", 1)
	switch tmp[0] {
	case "once":
	case "persist":
	case "retry":
		if n, err := strconv.Atoi(tmp[1]); err != nil || n < 0 {
			return false
		}
	default:
		return false
	}
	return true
}

func FormValueDefault(r *http.Request, k, d string) string {
	if s := r.FormValue(k); s != "" {
		return s
	} else {
		return d
	}
}

func TaskListHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.Header().Set("Allow", "GET")
		http.Error(w, http.StatusText(405), 405)
		return
	}
}

func TaskStatusHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.Header().Set("Allow", "GET")
		http.Error(w, http.StatusText(405), 405)
		return
	}
	taskID := getTaskIdFromURI(r.URL.Path)
	if taskID != "" {
		fmt.Fprintln(w, "task id:", taskID)
	}
	t, err := FindTaskById(taskID)
	if err != nil {
		http.Error(w, "task not found", 404)
		return
	}
	fmt.Fprintln(w, t)
}

func TaskDeleteHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "DELETE" {
		w.Header().Set("Allow", "DELETE")
		http.Error(w, http.StatusText(405), 405)
		return
	}
	taskID := getTaskIdFromURI(r.URL.Path)
	if taskID != "" {
		fmt.Fprintln(w, "task id:", taskID)
	}
}

func getTaskIdFromURI(uri string) string {
	taskID := uri[len("/api/v1/status/"):]
	if crontab_id.MatchString(taskID) {
		return taskID
	}
	return ""
}
<commit_msg>added method filter func to handlers<commit_after>// Copyright 2013 taskmaster authors.  All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package main

import (
	"fmt"
	"net/http"
	"regexp"
	"strconv"
	"strings"
)

var crontab_id = regexp.MustCompile(`^/([0-9]+)$`)

/* TaskCreateHandler creates new tasks. It accepts the following arguments:
- cron:		Cron string to schedule the task [required]
- src[url]:	URL that's hit when cron executes [required]
- src[method]:	HTTP method used for accessing the source URL [default: GET]
- src[body]:	Optional POST payload for the source URL [default: empty]
- src[policy]:	Policy for accessing the source URL [default: once]
- dst[url]:	URL that'll receive the task response [required]
- dst[policy]:	Policy for accessing the destination URL [default: once]

Supported policies are:
- once:		Try once and give up if it fails [default]
- persist:	Retry for ever until it works
- retry,N:	Use N as the number of times to retry before giving up
*/
func TaskCreateHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.Header().Set("Allow", "POST")
		http.Error(w, http.StatusText(405), 405)
		return
	}
	cron := r.FormValue("cron")
	tsrc := TaskSrc{
		URL:    r.FormValue("src[url]"),
		Method: FormValueDefault(r, "src[method]", "GET"),
		Body:   r.FormValue("src[body]"),
		Policy: FormValueDefault(r, "src[policy]", "once"),
	}
	tdst := TaskDst{
		URL:    r.FormValue("dst[url]"),
		Policy: FormValueDefault(r, "dst[policy]", "once"),
	}
	var errmsg string
	switch {
	case cron == "":
		errmsg = "Missing cron"
	case tsrc.URL == "":
		errmsg = "Missing src[url]"
	case !ValidMethod(tsrc.Method):
		errmsg = "Invalid src[method]"
	case !ValidPolicy(tsrc.Policy):
		errmsg = "Invalid src[policy]"
	case tsrc.Method == "GET" && tsrc.Body != "":
		errmsg = "Invalid src[method]=GET with non-empty src[body]"
	case tsrc.Method == "POST" && tsrc.Body == "":
		errmsg = "Invalid src[method]=POST with empty src[body]"
	case tdst.URL == "":
		errmsg = "Missing dst[url]"
	case !ValidPolicy(tdst.Policy):
		errmsg = "Invalid dst[policy]"
	}
	if errmsg != "" {
		http.Error(w, errmsg, 400)
		return
	}
	t, _ := NewTask(cron, tsrc, tdst)
	fmt.Println(t)
	fmt.Fprintf(w, "OK\r\n")
}

func ValidMethod(s string) bool {
	switch s {
	case "DELETE":
	case "GET":
	case "POST":
	case "PUT":
	default:
		return false
	}
	return true
}

func ValidPolicy(s string) bool {
	tmp := strings.SplitN(s, ",", 1)
	switch tmp[0] {
	case "once":
	case "persist":
	case "retry":
		if n, err := strconv.Atoi(tmp[1]); err != nil || n < 0 {
			return false
		}
	default:
		return false
	}
	return true
}

func FormValueDefault(r *http.Request, k, d string) string {
	if s := r.FormValue(k); s != "" {
		return s
	} else {
		return d
	}
}

func TaskListHandler(w http.ResponseWriter, r *http.Request) {
	if checkValidHTTPMethods([]string{"GET"}, w, r) == false {
		return
	}
}

func TaskStatusHandler(w http.ResponseWriter, r *http.Request) {
	if checkValidHTTPMethods([]string{"GET"}, w, r) == false {
		return
	}

	taskID := getTaskIdFromURI(r.URL.Path)
	if taskID != "" {
		fmt.Fprintln(w, "task id:", taskID)
	}
	t, err := FindTaskById(taskID)
	if err != nil {
		http.Error(w, "task not found", 404)
		return
	}
	fmt.Fprintln(w, t)
}

func TaskDeleteHandler(w http.ResponseWriter, r *http.Request) {
	if checkValidHTTPMethods([]string{"DELETE"}, w, r) == false {
		return
	}

	taskID := getTaskIdFromURI(r.URL.Path)
	if taskID != "" {
		fmt.Fprintln(w, "task id:", taskID)
	}
}

func getTaskIdFromURI(uri string) string {
	taskID := uri[len("/api/v1/status/"):]
	if crontab_id.MatchString(taskID) {
		return taskID
	}
	return ""
}

func checkValidHTTPMethods(allowed []string, w http.ResponseWriter, r *http.Request) bool {
	for _, t := range allowed {
		if t == r.Method {
			return true
		}
	}
	w.Header().Set("Allow", strings.Join(allowed, ", "))
	http.Error(w, http.StatusText(405), 405)
	return false
}
<|endoftext|>